Skip to content

Commit

Permalink
Updated Navigation Scopes
Browse files Browse the repository at this point in the history
  • Loading branch information
chRyNaN committed Dec 19, 2021
1 parent bb22daf commit 18b2d8f
Show file tree
Hide file tree
Showing 10 changed files with 143 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,29 @@ internal actual fun <T> InternalNavContainer(
) {
val contentKey by navigator.keyChanges.collectAsState(initial = navigator.initialKey)

val scope = object : ComposeNavigationContentScope<T> {

override val navigator: ComposeStackNavigatorByContent<T> = navigator
}

Box {
navigator.content(contentKey)
navigator.apply {
scope.content(contentKey)
}
}
}

@Composable
@ExperimentalNavigationApi
internal actual fun <T> InternalNavContainer(
navigator: BaseComposeNavigatorByKeyViewModel<T>
internal actual fun <T, S : ComposeNavigationKeyScope<T>> InternalNavContainer(
navigator: BaseComposeNavigatorByKeyViewModel<T, S>,
scope: S
) {
val contentKey by navigator.keyChanges.collectAsState(initial = navigator.initialKey)

Box {
navigator.content(contentKey)
navigator.apply {
scope.content(contentKey)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,33 @@ import kotlinx.coroutines.flow.filterNotNull
@ExperimentalNavigationApi
interface ComposeNavigationIntentStackNavigatorByKey<I : NavigationIntent> :
ComposeStackNavigatorByKey<I>,
NavigationEventHandler<I, ComposeNavigationScope>,
NavigationEventHandler<I, ComposeNavigationIntentScope<I>>,
NavigationEventNavigator<I> {

override fun ComposeNavigationScope.onGoBack() {
override fun ComposeNavigationIntentScope<I>.onGoBack() {
goBack()
}

override fun ComposeNavigationScope.onGoUp() = onGoBack()
override fun ComposeNavigationIntentScope<I>.onGoUp() = onGoBack()

override fun ComposeNavigationScope.onGoTo(intent: I) = goTo(key = intent)
override fun ComposeNavigationIntentScope<I>.onGoTo(intent: I) = goTo(key = intent)

override fun navigate(event: NavigationEvent<I>) {
ComposeNavigationScope.onNavigate(event = event)
val scope = object : ComposeNavigationIntentScope<I> {

override val navigator: ComposeNavigationIntentStackNavigatorByKey<I>
get() = this@ComposeNavigationIntentStackNavigatorByKey
}

scope.onNavigate(event = event)
}
}

@ExperimentalNavigationApi
class ComposeNavigationIntentNavigatorByKeyViewModel<I : NavigationIntent> internal constructor(
override val initialKey: I,
override val content: @Composable (key: I) -> Unit
) : BaseComposeNavigatorByKeyViewModel<I>(),
override val content: @Composable ComposeNavigationIntentScope<I>.(key: I) -> Unit
) : BaseComposeNavigatorByKeyViewModel<I, ComposeNavigationIntentScope<I>>(),
ComposeNavigationIntentStackNavigatorByKey<I> {

override val keyChanges: Flow<I>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,28 @@
package com.chrynan.navigation.compose

import com.chrynan.navigation.NavigationIntent
import com.chrynan.navigation.NavigationScope

@ExperimentalNavigationApi
object ComposeNavigationScope : NavigationScope
interface ComposeNavigationScope : NavigationScope {

companion object : ComposeNavigationScope
}

@ExperimentalNavigationApi
interface ComposeNavigationKeyScope<K> : ComposeNavigationScope {

val navigator: ComposeStackNavigatorByKey<K>
}

@ExperimentalNavigationApi
interface ComposeNavigationIntentScope<I : NavigationIntent> : ComposeNavigationKeyScope<I> {

override val navigator: ComposeNavigationIntentStackNavigatorByKey<I>
}

@ExperimentalNavigationApi
interface ComposeNavigationContentScope<K> : ComposeNavigationScope {

val navigator: ComposeStackNavigatorByContent<K>
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ interface ComposeNavigatorByContent<T> : ComposeNavigator<T> {
fun goTo(
key: T,
strategy: NavStackDuplicateContentStrategy,
content: @Composable () -> Unit
content: @Composable ComposeNavigationContentScope<T>.() -> Unit
)
}

// Note: This is needed because defaults aren't working for @Composable functions for interfaces.
@Suppress("unused")
@ExperimentalNavigationApi
@Composable
fun <T> ComposeNavigatorByContent<T>.goTo(key: T, content: @Composable () -> Unit) =
fun <T> ComposeNavigatorByContent<T>.goTo(key: T, content: @Composable ComposeNavigationContentScope<T>.() -> Unit) =
goTo(key = key, strategy = NavStackDuplicateContentStrategy.CLEAR_STACK, content = content)

@ExperimentalNavigationApi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ abstract class BaseComposeNavigatorByContentViewModel<T> : ViewModel(),
ComposeStackNavigatorByContent<T> {

@Composable
internal abstract fun content(key: T)
internal abstract fun ComposeNavigationContentScope<T>.content(key: T)
}

@ExperimentalNavigationApi
class ComposeNavigatorByContentViewModel<T> internal constructor(
override val initialKey: T,
private val initialContent: @Composable () -> Unit
private val initialContent: @Composable ComposeNavigationContentScope<T>.() -> Unit
) : BaseComposeNavigatorByContentViewModel<T>() {

override val keyChanges: Flow<T>
Expand All @@ -34,14 +34,14 @@ class ComposeNavigatorByContentViewModel<T> internal constructor(

private val mutableKeyFlow = MutableStateFlow<T?>(value = null)

private val contents = mutableMapOf<T, (@Composable () -> Unit)>()
private val contents = mutableMapOf<T, (@Composable ComposeNavigationContentScope<T>.() -> Unit)>()
private val keyStack = mutableListOf<T>()

@Composable
override fun goTo(
key: T,
strategy: NavStackDuplicateContentStrategy,
content: @Composable () -> Unit
content: @Composable ComposeNavigationContentScope<T>.() -> Unit
) {
if (contents.containsKey(key) && strategy == NavStackDuplicateContentStrategy.CLEAR_STACK) {
// Go Back to the content with the provided key using the updated content
Expand Down Expand Up @@ -80,19 +80,19 @@ class ComposeNavigatorByContentViewModel<T> internal constructor(
override fun canGoBack(): Boolean = contents.isNotEmpty() && keyStack.isNotEmpty()

@Composable
override fun content(key: T) {
override fun ComposeNavigationContentScope<T>.content(key: T) {
if (contents.isEmpty()) {
addToStack(key = key, content = initialContent)

isInitialized = true
}

contents[key]?.invoke()
contents[key]?.invoke(this)
}

private fun addToStack(
key: T,
content: @Composable () -> Unit
content: @Composable ComposeNavigationContentScope<T>.() -> Unit
) {
contents[key] = content
keyStack.add(key)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,19 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.filterNotNull

@ExperimentalNavigationApi
abstract class BaseComposeNavigatorByKeyViewModel<T> : ViewModel(),
abstract class BaseComposeNavigatorByKeyViewModel<T, S : ComposeNavigationKeyScope<T>> : ViewModel(),
ComposeNavigator<T>,
ComposeNavigatorByKey<T>,
ComposeStackNavigatorByKey<T> {

internal abstract val content: @Composable (key: T) -> Unit
internal abstract val content: @Composable S.(key: T) -> Unit
}

@ExperimentalNavigationApi
class ComposeNavigatorByKeyViewModel<T> internal constructor(
override val initialKey: T,
override val content: @Composable (key: T) -> Unit
) : BaseComposeNavigatorByKeyViewModel<T>() {
override val content: @Composable ComposeNavigationKeyScope<T>.(key: T) -> Unit
) : BaseComposeNavigatorByKeyViewModel<T, ComposeNavigationKeyScope<T>>() {

override val keyChanges: Flow<T>
get() = mutableKeyFlow.filterNotNull()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@
package com.chrynan.navigation.compose

import androidx.compose.runtime.Composable
import com.chrynan.navigation.NavigationIntent

@Composable
@ExperimentalNavigationApi
internal expect fun <T> InternalNavContainer(navigator: BaseComposeNavigatorByContentViewModel<T>)

@Composable
@ExperimentalNavigationApi
internal expect fun <T> InternalNavContainer(navigator: BaseComposeNavigatorByKeyViewModel<T>)
internal expect fun <T, S : ComposeNavigationKeyScope<T>> InternalNavContainer(
navigator: BaseComposeNavigatorByKeyViewModel<T, S>,
scope: S
)

/**
* Displays the content from a [navigator] in this [Composable] UI Container.
Expand All @@ -33,7 +37,7 @@ internal expect fun <T> InternalNavContainer(navigator: BaseComposeNavigatorByKe
*/
@Composable
@ExperimentalNavigationApi
fun <T> NavContainer(navigator: BaseComposeNavigatorByContentViewModel<T>) {
fun <T> NavContainer(navigator: ComposeNavigatorByContentViewModel<T>) {
InternalNavContainer(navigator = navigator)
}

Expand Down Expand Up @@ -61,10 +65,51 @@ fun <T> NavContainer(navigator: BaseComposeNavigatorByContentViewModel<T>) {
* ```
*
* @see [rememberNavigatorByKey]
*/
@Composable
@ExperimentalNavigationApi
fun <T> NavContainer(navigator: ComposeNavigatorByKeyViewModel<T>) {
val scope = object : ComposeNavigationKeyScope<T> {

override val navigator: ComposeStackNavigatorByKey<T>
get() = navigator
}

InternalNavContainer(navigator = navigator, scope = scope)
}

/**
* Displays the content from a [navigator] in this [Composable] UI Container.
*
* When the [navigator] changes its content, even outside this [NavContainer], it will be reflected within this UI
* container.
*
* Example usage:
* ```
* val navigator = rememberNavigatorByKey(HomeNavigationIntent.Greeting) { navigationIntent ->
* when(navigationIntent) {
* HomeNavigationIntent.Greeting -> Text("Hello")
* HomeNavigationIntent.Farewell -> Text("Good-bye")
* }
* }
*
* // The NavContainer will start by displaying the initial content, which in this case is "Hello"
* NavContainer(navigator)
*
* // The above NavContainer will display "Good Bye" after the following call:
* navigator.goTo(HomeNavigationIntent.Farewell)
* ```
*
* @see [rememberNavigatorByIntent]
*/
@Composable
@ExperimentalNavigationApi
fun <T> NavContainer(navigator: BaseComposeNavigatorByKeyViewModel<T>) {
InternalNavContainer(navigator = navigator)
fun <T : NavigationIntent> NavContainer(navigator: ComposeNavigationIntentNavigatorByKeyViewModel<T>) {
val scope = object : ComposeNavigationIntentScope<T> {

override val navigator: ComposeNavigationIntentStackNavigatorByKey<T>
get() = navigator
}

InternalNavContainer(navigator = navigator, scope = scope)
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import com.chrynan.navigation.Navigator
@Composable
fun <T> rememberNavigatorByContent(
initialKey: T,
initialContent: @Composable () -> Unit
initialContent: @Composable ComposeNavigationContentScope<T>.() -> Unit
): ComposeNavigatorByContentViewModel<T> = remember {
ComposeNavigatorByContentViewModel(
initialKey = initialKey,
Expand Down Expand Up @@ -74,7 +74,7 @@ fun <T> rememberNavigatorByContent(
@Composable
fun <T> rememberNavigatorByKey(
initialKey: T,
content: @Composable (key: T) -> Unit
content: @Composable ComposeNavigationKeyScope<T>.(key: T) -> Unit
): ComposeNavigatorByKeyViewModel<T> = remember {
ComposeNavigatorByKeyViewModel(
initialKey = initialKey,
Expand Down Expand Up @@ -114,7 +114,7 @@ fun <T> rememberNavigatorByKey(
@Composable
fun <I : NavigationIntent> rememberNavigatorByIntent(
initialIntent: I,
content: @Composable (intent: I) -> Unit
content: @Composable ComposeNavigationIntentScope<I>.(intent: I) -> Unit
): ComposeNavigationIntentNavigatorByKeyViewModel<I> = remember {
ComposeNavigationIntentNavigatorByKeyViewModel(
initialKey = initialIntent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,29 @@ internal actual fun <T> InternalNavContainer(
) {
val contentKey by navigator.keyChanges.collectAsState(initial = navigator.initialKey)

val scope = object : ComposeNavigationContentScope<T> {

override val navigator: ComposeStackNavigatorByContent<T> = navigator
}

Div {
navigator.content(contentKey)
navigator.apply {
scope.content(contentKey)
}
}
}

@Composable
@ExperimentalNavigationApi
internal actual fun <T> InternalNavContainer(
navigator: BaseComposeNavigatorByKeyViewModel<T>
internal actual fun <T, S : ComposeNavigationKeyScope<T>> InternalNavContainer(
navigator: BaseComposeNavigatorByKeyViewModel<T, S>,
scope: S
) {
val contentKey by navigator.keyChanges.collectAsState(initial = navigator.initialKey)

Div {
navigator.content(contentKey)
navigator.apply {
scope.content(contentKey)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,29 @@ internal actual fun <T> InternalNavContainer(
) {
val contentKey by navigator.keyChanges.collectAsState(initial = navigator.initialKey)

val scope = object : ComposeNavigationContentScope<T> {

override val navigator: ComposeStackNavigatorByContent<T> = navigator
}

Box {
navigator.content(contentKey)
navigator.apply {
scope.content(contentKey)
}
}
}

@Composable
@ExperimentalNavigationApi
internal actual fun <T> InternalNavContainer(
navigator: BaseComposeNavigatorByKeyViewModel<T>
internal actual fun <T, S : ComposeNavigationKeyScope<T>> InternalNavContainer(
navigator: BaseComposeNavigatorByKeyViewModel<T, S>,
scope: S
) {
val contentKey by navigator.keyChanges.collectAsState(initial = navigator.initialKey)

Box {
navigator.content(contentKey)
navigator.apply {
scope.content(contentKey)
}
}
}

0 comments on commit 18b2d8f

Please sign in to comment.