Compare commits

...

2 Commits

Author SHA1 Message Date
Claude
6f7fbb3ddb fix(nav): make canPop reactive so back-swipe transitions don't leave stale arrows
The previous fix added a start-destination short-circuit, but it only
helped if controller.currentBackStackEntry happened to be Home at the
moment canPop was called. During a back-swipe, the popping entry can
still be the current entry while Home is being revealed, so the
short-circuit didn't fire — and once the gesture finished, nothing
re-evaluated canPop, so the stale 'true' stuck.

Make canPop @Composable and read currentBackStackEntryAsState so call
sites recompose when the back stack settles after any navigation or
back-swipe transition. All existing callers (top bars, app bottom bar,
profile header, screen-level scaffolds) are already in @Composable
contexts, so no signature changes ripple out.

https://claude.ai/code/session_01PrirRcL7g8iX7vTqqLTkBS
2026-04-28 00:27:06 +00:00
Claude
dfc897abd3 fix(nav): never show back arrow on the graph's start destination
When swiping back from a drawer-pushed screen (e.g. Drafts) to Home,
Compose Navigation keeps the popping entry in the back stack until the
gesture animation finishes. If Home's top bar evaluates canPop during
that window, previousBackStackEntry is still the Drafts entry, so the
back arrow appears. canPop isn't observed reactively, so the stale
true sticks even after the pop completes — pressing the arrow then
does nothing useful (system back closes the app).

Short-circuit canPop when the current entry is the graph's start
destination. By construction, navBottomBar pops up to Home with
inclusive=false and nothing else routes below it, so Home has no valid
"back" target and the arrow should never render there.

https://claude.ai/code/session_01PrirRcL7g8iX7vTqqLTkBS
2026-04-27 21:21:38 +00:00
3 changed files with 21 additions and 2 deletions

View File

@@ -22,6 +22,7 @@ package com.vitorpamplona.amethyst.ui.navigation.navs
import androidx.compose.material3.DrawerState
import androidx.compose.material3.DrawerValue
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
import kotlinx.coroutines.CoroutineScope
@@ -46,6 +47,7 @@ class EmptyNav : INav {
override fun navBottomBar(route: Route) {}
@Composable
override fun canPop(): Boolean = false
override fun popBack() {}

View File

@@ -21,6 +21,7 @@
package com.vitorpamplona.amethyst.ui.navigation.navs
import androidx.compose.material3.DrawerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
import kotlinx.coroutines.CoroutineScope
@@ -43,6 +44,7 @@ interface INav {
fun navBottomBar(route: Route)
@Composable
fun canPop(): Boolean
fun popBack()

View File

@@ -23,8 +23,12 @@ package com.vitorpamplona.amethyst.ui.navigation.navs
import android.annotation.SuppressLint
import androidx.compose.material3.DrawerState
import androidx.compose.material3.DrawerValue
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.NavHostController
import androidx.navigation.compose.currentBackStackEntryAsState
import com.vitorpamplona.amethyst.ui.navigation.BOTTOM_NAV_ROOT_KEY
import com.vitorpamplona.amethyst.ui.navigation.isBottomNavRoot
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
@@ -93,9 +97,20 @@ class Nav(
}
}
@Composable
override fun canPop(): Boolean {
val current = controller.currentBackStackEntry ?: return false
if (current.isBottomNavRoot()) return false
// Observe the current entry as State so consumers recompose when the
// back stack settles after a navigation or back-swipe transition.
// A non-reactive read would leave a stale value behind: e.g. on
// back-swipe to Home, previousBackStackEntry is still the popping
// entry until the gesture finishes, and nothing would re-evaluate
// canPop afterwards.
val current by controller.currentBackStackEntryAsState()
val entry = current ?: return false
if (entry.isBottomNavRoot()) return false
// Home is the graph's start destination and nothing can sit below
// it, so a back arrow there is never meaningful.
if (entry.destination.id == controller.graph.findStartDestination().id) return false
return controller.previousBackStackEntry != null
}