feat(nav): Messages replaces Question Packs in bottom bar, conversation route with deep link, Play Hub hosts Packs

- AppNavigation: MESSAGES + CONVERSATION composable routes, bottom bar swaps Packs for Messages
- AppRoute: MESSAGES + CONVERSATION route constants, conversation() helper, bottom nav list updated
- PlayHubScreen: Question Packs card added (moved from bottom bar)
- AnswerRevealScreen: discuss button navigates to conversation route
- DailyQuestionScreen: discuss button navigates to conversation route
This commit is contained in:
null 2026-06-24 16:13:58 -05:00
parent 33baf220e4
commit c85e55a790
5 changed files with 42 additions and 5 deletions

View File

@ -3,6 +3,7 @@ package app.closer.core.navigation
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.automirrored.filled.Chat
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.PlayArrow
@ -66,6 +67,8 @@ import app.closer.ui.questions.QuestionCategoryScreen
import app.closer.ui.questions.QuestionComposerScreen
import app.closer.ui.questions.QuestionPackLibraryScreen
import app.closer.ui.questions.QuestionThreadScreen
import app.closer.ui.messages.MessagesInboxScreen
import app.closer.ui.messages.ConversationScreen
import app.closer.ui.settings.AccountScreen
import app.closer.ui.settings.AppearanceScreen
import app.closer.ui.settings.DeleteAccountScreen
@ -240,6 +243,21 @@ fun AppNavigation(
composable(route = AppRoute.QUESTION_PACKS) {
QuestionPackLibraryScreen(onNavigate = navigateRoute)
}
composable(route = AppRoute.MESSAGES) {
MessagesInboxScreen(onNavigate = navigateRoute)
}
composable(
route = AppRoute.CONVERSATION,
arguments = listOf(
navArgument("coupleId") { type = NavType.StringType },
navArgument("conversationId") { type = NavType.StringType }
),
deepLinks = listOf(
navDeepLink { uriPattern = "closer://closer.app/conversation/{coupleId}/{conversationId}" }
)
) {
ConversationScreen(onNavigate = navigateRoute)
}
composable(
route = AppRoute.QUESTION_CATEGORY,
arguments = listOf(navArgument("categoryId") { type = NavType.StringType })
@ -517,7 +535,7 @@ fun AppNavigation(
// Floating in-app chat-head for incoming partner messages — drifts over every screen,
// draggable, tap to open the conversation.
app.closer.ui.components.MessageBubbleOverlay(
onOpen = { c, q -> navigateRoute(AppRoute.questionThread(c, q)) }
onOpen = { c, conv -> navigateRoute(AppRoute.conversation(c, conv)) }
)
}
}
@ -532,7 +550,7 @@ private val topLevelRoutes = listOf(
TopLevelRoute(AppRoute.HOME, "Home", Icons.Filled.Home),
TopLevelRoute(AppRoute.DAILY_QUESTION, "Today", Icons.Filled.Favorite),
TopLevelRoute(AppRoute.PLAY, "Play", Icons.Filled.PlayArrow),
TopLevelRoute(AppRoute.QUESTION_PACKS, "Packs", Icons.Filled.Star),
TopLevelRoute(AppRoute.MESSAGES, "Messages", Icons.AutoMirrored.Filled.Chat),
TopLevelRoute(AppRoute.SETTINGS, "Settings", Icons.Filled.Settings)
)

View File

@ -14,6 +14,8 @@ object AppRoute {
const val PLAY = "play"
const val DAILY_QUESTION = "daily_question"
const val QUESTION_PACKS = "question_packs"
const val MESSAGES = "messages"
const val CONVERSATION = "conversation/{coupleId}/{conversationId}"
const val QUESTION_CATEGORY = "question_category/{categoryId}"
const val QUESTION_COMPOSER = "question_composer"
const val ANSWER_REVEAL = "answer_reveal/{questionId}"
@ -85,6 +87,8 @@ object AppRoute {
Definition(PLAY, "Play", "play"),
Definition(DAILY_QUESTION, "Daily Question", "questions"),
Definition(QUESTION_PACKS, "Question Packs", "questions"),
Definition(MESSAGES, "Messages", "messages"),
Definition(CONVERSATION, "Conversation", "messages"),
Definition(QUESTION_CATEGORY, "Question Pack", "questions"),
Definition(QUESTION_COMPOSER, "New Question", "questions"),
Definition(QUESTION_THREAD, "Answer", "questions"),
@ -132,7 +136,7 @@ object AppRoute {
HOME,
DAILY_QUESTION,
PLAY,
QUESTION_PACKS,
MESSAGES,
SETTINGS
)
@ -191,6 +195,9 @@ object AppRoute {
fun answerReveal(questionId: String): String = "answer_reveal/${questionId.asRouteArg()}"
fun conversation(coupleId: String, conversationId: String): String =
"conversation/${coupleId.asRouteArg()}/${conversationId.asRouteArg()}"
fun inviteConfirm(inviteCode: String): String = "invite_confirm/${inviteCode.asRouteArg()}"
fun questionCategory(categoryId: String): String = "question_category/${categoryId.asRouteArg()}"

View File

@ -92,7 +92,7 @@ fun AnswerRevealScreen(
onAnswerQuestion = {
val coupleId = state.coupleId
if (coupleId != null) {
onNavigate(AppRoute.questionThread(coupleId, questionId))
onNavigate(AppRoute.conversation(coupleId, "q_" + questionId))
} else {
// Discussing requires a paired partner; send unpaired users to invite one.
onNavigate(AppRoute.CREATE_INVITE)

View File

@ -144,6 +144,18 @@ private fun PlayHubContent(
)
}
// Question Packs — moved here from the bottom bar (which now hosts Messages).
item {
CompactPlayCard(
title = "Question Packs",
subtitle = "Themed prompts to explore together",
icon = Icons.Filled.Star,
tint = MaterialTheme.colorScheme.primary,
modifier = Modifier.fillMaxWidth(),
onClick = { onNavigate(AppRoute.QUESTION_PACKS) }
)
}
item {
Row(
modifier = Modifier.fillMaxWidth(),

View File

@ -40,7 +40,7 @@ fun DailyQuestionScreen(
onPrimaryRoute = { question ->
val coupleId = state.coupleId
if (coupleId != null) {
onNavigate(AppRoute.questionThread(coupleId, question.id))
onNavigate(AppRoute.conversation(coupleId, "q_" + question.id))
} else {
onNavigate(AppRoute.CREATE_INVITE)
}