Skip to content

Commit

Permalink
Change API to access all host / scoped host entries through the state
Browse files Browse the repository at this point in the history
This is more similar to the NavHostScope interface now and should be easier to understand and use.
  • Loading branch information
olshevski committed Jul 24, 2023
1 parent 50b110f commit 7aa2257
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 44 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
package dev.olshevski.navigation.reimagined

internal fun <T, S> NavHostStateImpl<T, S>.findHostEntry(destination: T) =
hostEntriesMap.values.find { it.destination == destination }!!
@OptIn(ExperimentalReimaginedApi::class)
internal fun <T> NavHostState<T>.findHostEntry(destination: T) =
hostEntries.find { it.destination == destination }!!
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ class SavedStateHandleTest(
}

lateinit var screenController: NavController<Screen>
internal lateinit var screenState: NavHostStateImpl<Screen, *>
internal lateinit var screenState: NavHostState<Screen>

lateinit var subScreenController: NavController<SubScreen>
internal lateinit var subScreenState: NavHostStateImpl<SubScreen, *>
Expand All @@ -93,10 +93,8 @@ class SavedStateHandleTest(

setContent {
screenController = rememberNavController(Screen.A)
screenState = rememberNavHostStateImpl(
backstack = screenController.backstack,
scopeSpec = EmptyScopeSpec
)
@OptIn(ExperimentalReimaginedApi::class)
screenState = rememberNavHostState(screenController.backstack,)
ParamNavHost(hostParam, screenState) { screen ->
paramViewModel(
factoryParam = factoryParam,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import androidx.annotation.VisibleForTesting
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.SaveableStateHolder
Expand Down Expand Up @@ -44,7 +45,7 @@ import kotlinx.parcelize.Parcelize
@Composable
fun <T> rememberNavHostState(
backstack: NavBackstack<T>
): NavHostState<T> = rememberScopingNavHostState(backstack, EmptyScopeSpec)
): NavHostState<T> = rememberNavHostStateImpl(backstack, EmptyScopeSpec)

/**
* Remembers [ScopingNavHostState]. This allows you to hoist the state of ScopingNavHost and
Expand Down Expand Up @@ -130,23 +131,16 @@ internal fun <T, S> rememberNavHostStateImpl(
* ViewModelStore, SavedStateRegistry) for every entry.
*/
@Stable
sealed interface NavHostState<T> {
sealed interface NavHostState<out T> {

/**
* The current backstack.
*/
val backstack: NavBackstack<T>

/**
* Get [NavHostEntry] for the specified [id]. If there is no entry with this id
* in the current [backstack], `null` will be returned.
* List of all current [NavHostEntries][NavHostEntry] in the same order their associated
* [entries][NavEntry] appear in the backstack.
*
* This method is intended to provide access to NavHostEntries outside of the current NavHost,
* e.g. for communication between several NavHosts. If you want to access NavHostEntries
* in the current NavHost you should access them through [NavHostScope].
* The last entry of this list is always the currently displayed entry.
*/
@ExperimentalReimaginedApi
fun getHostEntry(id: NavId): NavHostEntry<T>?
val hostEntries: List<NavHostEntry<T>>

}

Expand All @@ -155,19 +149,17 @@ sealed interface NavHostState<T> {
* ViewModelStore, SavedStateRegistry) for every entry and every scope.
*/
@Stable
sealed interface ScopingNavHostState<T, S> : NavHostState<T> {
sealed interface ScopingNavHostState<out T, S> : NavHostState<T> {

/**
* Get [ScopedNavHostEntry] for the specified [scope]. If there is no entry
* associated with this scope in the current [backstack], `null` will be returned.
* [ScopedNavHostEntries][ScopedNavHostEntry] for all scopes present in the backstack.
*
* This method is intended to provide access to ScopedNavHostEntries outside of the current
* ScopingNavHost, e.g. for communication between several NavHosts. If you want to access
* ScopedNavHostEntries in the current ScopingNavHost you should access them through
* [ScopingNavHostScope].
* Note that unlike [ScopingNavHostScope.scopedHostEntries], this property provides access to
* ALL scopes that are associated with current destinations in the backstack, not just the last
* (current) destination.
*/
@ExperimentalReimaginedApi
fun getScopedHostEntry(scope: S): ScopedNavHostEntry<S>?
val scopedHostEntries: Map<S, ScopedNavHostEntry<S>>

}

Expand All @@ -186,10 +178,9 @@ internal class NavHostStateImpl<T, S>(

val hostId: NavHostId = savedState?.hostId ?: NavHostId()

override var backstack by mutableStateOf(initialBackstack)
var backstack by mutableStateOf(initialBackstack)

@VisibleForTesting
val hostEntriesMap = mutableMapOf<NavId, NavHostEntry<T>>()
private val hostEntriesMap = mutableMapOf<NavId, NavHostEntry<T>>()

private val scopedHostEntriesMap = mutableMapOf<S, ScopedNavHostEntry<S>>()

Expand Down Expand Up @@ -268,7 +259,7 @@ internal class NavHostStateImpl<T, S>(
.mapNotNull { hostEntriesMap.remove(it) }

val backstackEntryScopes = snapshot.items
.flatMap { it.scopedHostEntries.keys }.toHashSet()
.flatMapTo(hashSetOf()) { it.scopedHostEntries.keys }
val outdatedScopedHostEntries = scopedHostEntriesMap.keys
.filter { it !in backstackEntryScopes }
.mapNotNull { scopedHostEntriesMap.remove(it) }
Expand Down Expand Up @@ -393,7 +384,7 @@ internal class NavHostStateImpl<T, S>(
outdatedHostEntryIds = outdatedHostEntriesQueue.getAllHostEntries().map { it.id }
)

@ExperimentalReimaginedApi
@InternalReimaginedApi
fun clear() {
getAllHostEntries().forEach { entry ->
entry.maxLifecycleState = Lifecycle.State.DESTROYED
Expand All @@ -405,24 +396,22 @@ internal class NavHostStateImpl<T, S>(
}

@ExperimentalReimaginedApi
override fun getHostEntry(id: NavId): NavHostEntry<T>? {
val entry = backstack.entries.find { it.id == id }
return entry?.let {
override val hostEntries: List<NavHostEntry<T>> by derivedStateOf {
backstack.entries.map { entry ->
hostEntriesMap.getOrPut(entry.id) {
newHostEntry(entry)
newHostEntry(entry = entry)
}
}
}

@ExperimentalReimaginedApi
override fun getScopedHostEntry(scope: S): ScopedNavHostEntry<S>? {
return if (backstack.entries.any { scope in scopeSpec.getScopes(it.destination) }) {
scopedHostEntriesMap.getOrPut(scope) {
newScopedHostEntry(id = NavId(), scope = scope)
override val scopedHostEntries: Map<S, ScopedNavHostEntry<S>> by derivedStateOf {
backstack.entries.flatMapTo(hashSetOf()) { scopeSpec.getScopes(it.destination) }
.associateWith { scope ->
scopedHostEntriesMap.getOrPut(scope) {
newScopedHostEntry(id = NavId(), scope = scope)
}
}
} else {
null
}
}

}
Expand Down

0 comments on commit 7aa2257

Please sign in to comment.