feat(nav): root swipe-back on chat with double-back exit and subpage hierarchy (#1001)
Co-authored-by: M57 <hy4ri@users.noreply.github.com>
This commit is contained in:
1 parent
f04d878358
commit
2332bdd5d5
7 files changed
+141
-101
No files matched your search
@@ -1,5 +1,7 @@
|
||||
package com.m57.hermescontrol
|
||||
|
||||
import android.app.Activity
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
@@ -31,10 +33,13 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableLongStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -191,7 +196,25 @@ fun MainNavigation(sessionId: String? = null) {
|
||||
|
||||
val openDrawer: () -> Unit = { scope.launch { drawerState.open() } }
|
||||
|
||||
val context = LocalContext.current
|
||||
var lastBackPressTime by remember { mutableLongStateOf(0L) }
|
||||
val backToastText = stringResource(R.string.press_back_again_to_exit)
|
||||
|
||||
// Double-back to exit on root ChatScreen
|
||||
BackHandler(enabled = currentScreen == ChatScreen && backStack.size == 1) {
|
||||
val now = System.currentTimeMillis()
|
||||
if (now - lastBackPressTime < 2000L) {
|
||||
(context as? Activity)?.finish()
|
||||
} else {
|
||||
lastBackPressTime = now
|
||||
Toast.makeText(context, backToastText, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
// Safety fallback: if a non-chat screen somehow sits alone on stack, swipe back returns to ChatScreen
|
||||
BackHandler(
|
||||
enabled = currentScreen != ChatScreen && currentScreen != LandingScreen && backStack.size == 1,
|
||||
) {
|
||||
NavigationController.goBack()
|
||||
}
|
||||
|
||||
|
||||
@@ -31,30 +31,37 @@ object NavigationController {
|
||||
|
||||
private var nextChatNavigationRequestId = 0L
|
||||
|
||||
// Top-level primary screens (Chat, Skills, Cron, System, Settings)
|
||||
private val primaryScreens: MutableSet<NavKey> =
|
||||
mutableSetOf(
|
||||
ChatScreen,
|
||||
SkillsScreen,
|
||||
CronJobsScreen,
|
||||
SystemScreen,
|
||||
SettingsScreen,
|
||||
)
|
||||
|
||||
/** Returns whether the given key is a primary top-level screen. */
|
||||
fun isPrimaryScreen(key: NavKey): Boolean = key in primaryScreens
|
||||
/**
|
||||
* Top-level primary screens (all drawer-accessible screens).
|
||||
* Navigating to any of these clears the stack to `[ChatScreen, key]` (or `[ChatScreen]` for Chat),
|
||||
* ensuring swiping back returns to ChatScreen.
|
||||
*/
|
||||
fun isPrimaryScreen(key: NavKey): Boolean = key == ChatScreen || ScreenRegistry.ALL_SCREENS.any { it.key == key }
|
||||
|
||||
fun navigateTo(key: NavKey) {
|
||||
val stack = backStack ?: return
|
||||
if (stack.lastOrNull() == key) return
|
||||
|
||||
if (key == ChatScreen) {
|
||||
stack.clear()
|
||||
stack.add(ChatScreen)
|
||||
return
|
||||
}
|
||||
|
||||
if (key == LandingScreen) {
|
||||
stack.clear()
|
||||
stack.add(LandingScreen)
|
||||
return
|
||||
}
|
||||
|
||||
if (isPrimaryScreen(key)) {
|
||||
stack.clear()
|
||||
stack.add(ChatScreen)
|
||||
stack.add(key)
|
||||
return
|
||||
}
|
||||
// Drawer dismissal is handled by DrawerGestureController (issue #619):
|
||||
// when a non-gesture sub-page composes, its HermesScaffold reconciles
|
||||
// drawerGesturesEnabled=false and the controller closes the drawer
|
||||
// itself via SideEffect. No synchronous closeDrawer callback here.
|
||||
|
||||
// Subscreen / drill-down navigation: append to current stack
|
||||
stack.add(key)
|
||||
}
|
||||
|
||||
@@ -98,15 +105,13 @@ object NavigationController {
|
||||
*/
|
||||
fun goBack(fallback: NavKey = ChatScreen) {
|
||||
val stack = backStack ?: return
|
||||
if (stack.lastOrNull() == ChatScreen) {
|
||||
resetTo(HistoryScreen)
|
||||
return
|
||||
}
|
||||
if (stack.size > 1) {
|
||||
stack.removeLastOrNull()
|
||||
} else if (stack.size == 1) {
|
||||
stack.clear()
|
||||
stack.add(fallback)
|
||||
if (stack.lastOrNull() != fallback && stack.lastOrNull() != LandingScreen) {
|
||||
stack.clear()
|
||||
stack.add(fallback)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user