Skip to content

Commit

Permalink
Updated BaseComposeNavigatorByKeyViewModel to implement ComposeScoped…
Browse files Browse the repository at this point in the history
…Navigator interface
  • Loading branch information
chRyNaN committed Dec 31, 2021
1 parent 03059dc commit 04b589f
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 105 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@file:Suppress("unused")

package com.chrynan.navigation.compose

internal sealed class ComposeNavigationEvent<Scope, Key>

internal data class ScopeEvent<Scope, Key>(val value: Scope) : ComposeNavigationEvent<Scope, Key>()

internal data class KeyEvent<Scope, Key>(val value: Key) : ComposeNavigationEvent<Scope, Key>()
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@ package com.chrynan.navigation.compose

import androidx.compose.runtime.Composable
import com.chrynan.navigation.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.filterNotNull

@ExperimentalNavigationApi
interface ComposeNavigationIntentStackNavigatorByKey<I : NavigationIntent> :
Expand Down Expand Up @@ -32,63 +29,11 @@ interface ComposeNavigationIntentStackNavigatorByKey<I : NavigationIntent> :
}

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

override val keyChanges: Flow<I>
get() = mutableKeyFlow.filterNotNull()

override val currentKey: I
get() = mutableKeyFlow.value

override val isInitialized: Boolean = true

private val mutableKeyFlow = MutableStateFlow(value = initialKey)

private val keyStack = mutableListOf<I>()

override fun goTo(key: I, strategy: NavStackDuplicateContentStrategy) {
if (key == currentKey) return

if (keyStack.contains(key) && strategy == NavStackDuplicateContentStrategy.CLEAR_STACK) {
// Go Back to the content with the provided key using the updated content
var lastKey = keyStack.lastOrNull()

while (lastKey != null && lastKey != key) {
keyStack.removeLast()
lastKey = keyStack.lastOrNull()
}

// Replace the content with the updated content
mutableKeyFlow.value = key
} else {
// Go to the provided content
addToStack(key = key)
}
}

override fun goBack(): Boolean {
val wentBack = canGoBack()

if (wentBack) {
removeLastFromStack()
}

return wentBack
}

override fun canGoBack(): Boolean = keyStack.size > 1

private fun addToStack(key: I) {
keyStack.add(key)
mutableKeyFlow.value = key
}

private fun removeLastFromStack() {
keyStack.removeLast()
mutableKeyFlow.value = keyStack.last()
}
}
class ComposeNavigationIntentNavigatorByKeyViewModel<Scope, Intent : NavigationIntent> internal constructor(
initialScope: Scope,
initialKeys: (Scope) -> Intent,
override val content: @Composable ComposeNavigationIntentScope<Intent>.(key: Intent) -> Unit
) : BaseComposeNavigatorByKeyViewModel<Scope, Intent, ComposeNavigationIntentScope<Intent>>(
initialScope = initialScope,
initialKeys = initialKeys
), ComposeNavigationIntentStackNavigatorByKey<Intent>
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,22 @@ interface ComposeNavigationScope : NavigationScope {
interface ComposeNavigationKeyScope<K> : ComposeNavigationScope {

val navigator: ComposeStackNavigatorByKey<K>

companion object
}

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

override val navigator: ComposeNavigationIntentStackNavigatorByKey<I>

companion object
}

@ExperimentalNavigationApi
interface ComposeNavigationContentScope<K> : ComposeNavigationScope {

val navigator: ComposeStackNavigatorByContent<K>

companion object
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,71 +8,109 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.filterNotNull

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

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

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

override val keyChanges: Flow<T>
abstract class BaseComposeNavigatorByKeyViewModel<Scope, Key, NavigationScope : ComposeNavigationKeyScope<Key>>(
final override val initialScope: Scope,
private val initialKeys: (Scope) -> Key
) : ViewModel(),
ComposeNavigator<Key>,
ComposeNavigatorByKey<Key>,
ComposeStackNavigatorByKey<Key>,
ComposeScopedNavigator<Scope, Key> {

internal abstract val content: @Composable NavigationScope.(key: Key) -> Unit

override val keyChanges: Flow<Key>
get() = mutableKeyFlow.filterNotNull()

override val currentKey: T
final override val initialKey: Key
get() = initialKeys(initialScope)

override val currentKey: Key
get() = mutableKeyFlow.value

override val isInitialized: Boolean = true

override val currentScope: Scope
get() = mutableScopeFlow.value

override val scopeChanges: Flow<Scope>
get() = mutableScopeFlow.filterNotNull()

private val mutableKeyFlow = MutableStateFlow(value = initialKey)
private val mutableScopeFlow = MutableStateFlow(value = initialScope)

private val scopedKeyStack = mutableMapOf(initialScope to mutableListOf(initialKey))

private val keyStack = mutableListOf<T>()
override fun goTo(key: Key, strategy: NavStackDuplicateContentStrategy) {
val currentScope = this.currentScope
val currentKeyStack = scopedKeyStack[currentScope] ?: mutableListOf()

override fun goTo(key: T, strategy: NavStackDuplicateContentStrategy) {
if (key == currentKey) return
// If we are already displaying this key on the current scoped stack, then return.
if (key == currentKeyStack.lastOrNull()) return

if (keyStack.contains(key) && strategy == NavStackDuplicateContentStrategy.CLEAR_STACK) {
if (strategy == NavStackDuplicateContentStrategy.CLEAR_STACK && currentKeyStack.contains(key)) {
// Go Back to the content with the provided key using the updated content
var lastKey = keyStack.lastOrNull()
var lastKey = currentKeyStack.lastOrNull()

while (lastKey != null && lastKey != key) {
keyStack.removeLast()
lastKey = keyStack.lastOrNull()
currentKeyStack.removeLast()
lastKey = currentKeyStack.lastOrNull()
}

// Replace the content with the updated content
scopedKeyStack[currentScope] = currentKeyStack
mutableKeyFlow.value = key
} else {
// Go to the provided content
addToStack(key = key)
currentKeyStack.add(key)
scopedKeyStack[currentScope] = currentKeyStack
mutableKeyFlow.value = key
}
}

override fun goBack(): Boolean {
val wentBack = canGoBack()

if (wentBack) {
removeLastFromStack()
val currentKeyStack = scopedKeyStack[currentScope] ?: mutableListOf()
currentKeyStack.removeLast()
mutableKeyFlow.value = currentKeyStack.last()
}

return wentBack
}

override fun canGoBack(): Boolean = keyStack.size > 1
override fun canGoBack(): Boolean {
val currentKeyStack = scopedKeyStack[currentScope] ?: mutableListOf()

private fun addToStack(key: T) {
keyStack.add(key)
mutableKeyFlow.value = key
return currentKeyStack.size > 1
}

private fun removeLastFromStack() {
keyStack.removeLast()
mutableKeyFlow.value = keyStack.last()
override fun changeScope(to: Scope) {
if (to == currentScope) return

val keyStack = scopedKeyStack[to]

if (keyStack.isNullOrEmpty()) {
val key = initialKeys(to)
val newKeyStack = mutableListOf(key)
scopedKeyStack[to] = newKeyStack
mutableScopeFlow.value = to
mutableKeyFlow.value = key
} else {
val key = keyStack.last()
mutableScopeFlow.value = to
mutableKeyFlow.value = key
}
}
}

@ExperimentalNavigationApi
class ComposeNavigatorByKeyViewModel<Scope, Key> internal constructor(
initialScope: Scope,
initialKeys: (Scope) -> Key,
override val content: @Composable ComposeNavigationKeyScope<Key>.(key: Key) -> Unit
) : BaseComposeNavigatorByKeyViewModel<Scope, Key, ComposeNavigationKeyScope<Key>>(
initialScope = initialScope,
initialKeys = initialKeys
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@file:Suppress("unused")

package com.chrynan.navigation.compose

enum class NavBackStrategy {

EVENT,
STACK;

companion object
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,13 @@ fun <T> rememberNavigatorByContent(
*/
@ExperimentalNavigationApi
@Composable
fun <T> rememberNavigatorByKey(
initialKey: T,
content: @Composable ComposeNavigationKeyScope<T>.(key: T) -> Unit
): ComposeNavigatorByKeyViewModel<T> = remember {
fun <Key> rememberNavigatorByKey(
initialKey: Key,
content: @Composable ComposeNavigationKeyScope<Key>.(key: Key) -> Unit
): ComposeNavigatorByKeyViewModel<Nothing?, Key> = remember {
ComposeNavigatorByKeyViewModel(
initialKey = initialKey,
initialScope = null,
initialKeys = { initialKey },
content = content
)
}
Expand Down Expand Up @@ -112,12 +113,13 @@ fun <T> rememberNavigatorByKey(
*/
@ExperimentalNavigationApi
@Composable
fun <I : NavigationIntent> rememberNavigatorByIntent(
initialIntent: I,
content: @Composable ComposeNavigationIntentScope<I>.(intent: I) -> Unit
): ComposeNavigationIntentNavigatorByKeyViewModel<I> = remember {
fun <Intent : NavigationIntent> rememberNavigatorByIntent(
initialIntent: Intent,
content: @Composable ComposeNavigationIntentScope<Intent>.(intent: Intent) -> Unit
): ComposeNavigationIntentNavigatorByKeyViewModel<Nothing?, Intent> = remember {
ComposeNavigationIntentNavigatorByKeyViewModel(
initialKey = initialIntent,
initialScope = null,
initialKeys = { initialIntent },
content = content
)
}

0 comments on commit 04b589f

Please sign in to comment.