Skip to content

Commit

Permalink
Merge pull request #50 from InsanusMokrassar/0.5.7
Browse files Browse the repository at this point in the history
0.5.7
  • Loading branch information
InsanusMokrassar authored Oct 5, 2024
2 parents fae87b7 + 9bf1662 commit e50488c
Show file tree
Hide file tree
Showing 9 changed files with 137 additions and 44 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Navigation Changelog

## 0.5.7

**THIS UPDATE CONTAINS BREAKING CHANGES RELATED TO `NavigationNodeFactory` things**

* `Code`:
* Replace `NavigationNodeFactory.Companion.Typed` factory into `NavigationNodeFactory.Typed`
* Change order of `NavigationNodeFactory.Typed` type args
* Add default aggregator for `NavigationNodeFactory`
* Add extension `changesInSubtreeFlow` for either chains and nodes

## 0.5.6

* `Versions`:
Expand Down
2 changes: 1 addition & 1 deletion core/src/commonMain/kotlin/NavigationNode.kt
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ abstract class NavigationNode<Config : Base, Base>(
override val configState: StateFlow<T> = MutableStateFlow(config).asStateFlow()

companion object {
val DefaultFactory = NavigationNodeFactory.Companion.Typed<Any, Config> { chain, config ->
val DefaultFactory = NavigationNodeFactory.Typed<Config, Any?> { chain, config ->
Empty(chain, config)
}
}
Expand Down
50 changes: 28 additions & 22 deletions core/src/commonMain/kotlin/NavigationNodeFactory.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,36 @@ fun interface NavigationNodeFactory<Base> {
config: Base
): NavigationNode<out Base, Base>?

companion object {
open class Typed<Base : Any, T : Base>(
private val kClass: KClass<T>,
private val block: (navigationChain: NavigationChain<Base>, config: T) -> NavigationNode<T, Base>
) : NavigationNodeFactory<Base> {
override fun createNode(
navigationChain: NavigationChain<Base>,
config: Base
): NavigationNode<out Base, Base>? {
return if (kClass.isInstance(config)) {
block(navigationChain, config as T)
} else {
null
}
open class Typed<T : Base, Base : Any?>(
private val kClass: KClass<*>,
private val block: (navigationChain: NavigationChain<Base>, config: T) -> NavigationNode<T, Base>
) : NavigationNodeFactory<Base> {
override fun createNode(
navigationChain: NavigationChain<Base>,
config: Base
): NavigationNode<out Base, Base>? {
return if (kClass.isInstance(config)) {
block(navigationChain, config as T)
} else {
null
}
}

companion object {
inline operator fun <Base : Any, reified T : Base> invoke(
noinline block: (navigationChain: NavigationChain<Base>, config: T) -> NavigationNode<T, Base>
) = Typed<Base, T>(
kClass = T::class,
block
)
}
companion object {
inline operator fun <reified T : Base, Base : Any?> invoke(
noinline block: (navigationChain: NavigationChain<Base>, config: T) -> NavigationNode<T, Base>
) = Typed<T, Base>(
kClass = T::class,
block
)
}
}

open class DefaultAggregator<Base>(
private val factories: List<NavigationNodeFactory<Base>>,
) : NavigationNodeFactory<Base> {
override fun createNode(navigationChain: NavigationChain<Base>, config: Base): NavigationNode<out Base, Base>? {
return factories.firstNotNullOfOrNull { it.createNode(navigationChain, config) }
}
}
}
10 changes: 5 additions & 5 deletions core/src/commonMain/kotlin/extensions/ChainSubChanges.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,8 @@ import dev.inmo.navigation.core.NavigationChain
import dev.inmo.navigation.core.NavigationNode
import dev.inmo.navigation.core.NavigationNodeId
import dev.inmo.navigation.core.onDestroyFlow
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.job
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

fun <Base> NavigationChain<Base>.onChangesInSubTree(
scope: CoroutineScope,
Expand Down Expand Up @@ -59,6 +56,9 @@ fun <Config : Base, Base> NavigationNode<Config, Base>.onChangesInSubTree(
)
onChangeInSubNode(this@onChangesInSubTree, it)
}
configState.subscribeSafelyWithoutExceptions(subscope) {

}

onDestroyFlow.subscribeSafelyWithoutExceptions(subscope) {
subscope.cancel()
Expand Down
55 changes: 55 additions & 0 deletions core/src/commonMain/kotlin/extensions/ChangesInSubtreeFlow.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package dev.inmo.navigation.core.extensions

import dev.inmo.micro_utils.common.*
import dev.inmo.navigation.core.NavigationChain
import dev.inmo.navigation.core.NavigationNode
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge

/**
* @return [Flow] which will have one of two data variants: chain where happen changes and its stack list (list will be
* presented only if change happen there); node and its subchaines (subchaines will be presented only if their list
* have been changed)
*/
@Warning("This API is still experimental. Please, report on any wrong behaviour if any")
@OptIn(ExperimentalCoroutinesApi::class)
fun <Base> Either<NavigationChain<Base>, NavigationNode<out Base, Base>>.changesInSubtreeFlow(): Flow<
Either<
Pair<NavigationChain<Base>, List<NavigationNode<out Base, Base>>>,
Pair<NavigationNode<out Base, Base>, List<NavigationChain<Base>>?>,
>
> {
return merge(
when (this) {
is EitherFirst -> merge(
// chain
this.t1.stackFlow.map { (this.t1 to it).either() },
this.t1.stackFlow.flatMapLatest {
it.map {
it.either<NavigationChain<Base>, NavigationNode<out Base, Base>>().changesInSubtreeFlow<Base>()
}.merge()
},
)

is EitherSecond -> merge(
// node
this.t2.subchainsFlow.map { (this.t2 to it).either() },
this.t2.subchainsFlow.flatMapLatest {
it.map {
it.either<NavigationChain<Base>, NavigationNode<out Base, Base>>().changesInSubtreeFlow<Base>()
}.merge()
},
this.t2.stateChangesFlow.map { (this.t2 to null).either() },
this.t2.configState.map { (this.t2 to null).either() },
)
},
)
}

@Warning("This API is still experimental. Please, report on any wrong behaviour if any")
fun <Base> NavigationChain<Base>.changesInSubTreeFlow() = either<NavigationChain<Base>, NavigationNode<out Base, Base>>().changesInSubtreeFlow()
@Warning("This API is still experimental. Please, report on any wrong behaviour if any")
fun <Base> NavigationNode<out Base, Base>.changesInSubTreeFlow() = either<NavigationChain<Base>, NavigationNode<out Base, Base>>().changesInSubtreeFlow()
23 changes: 23 additions & 0 deletions core/src/commonMain/kotlin/tmp_utils/FlowDiff.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package dev.inmo.navigation.core.tmp_utils

import dev.inmo.micro_utils.common.Diff
import dev.inmo.micro_utils.common.Warning
import dev.inmo.micro_utils.common.diff
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map

@Warning("This feature can be removed in any update without notice")
val <T> Flow<List<T>>.diffFlow: Flow<Diff<T>>
get() = flow {
var currentValue = first()

map {
val diff = it.diff(currentValue)

currentValue = it

emit(diff)
}
}
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ org.gradle.jvmargs=-Xmx1g
# Project data

group=dev.inmo
version=0.5.6
android_code_version=45
version=0.5.7
android_code_version=46
13 changes: 10 additions & 3 deletions sample/src/commonMain/kotlin/CommonPlugin.kt
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
package dev.inmo.navigation.sample

import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import dev.inmo.micro_utils.common.either
import dev.inmo.micro_utils.koin.singleWithRandomQualifier
import dev.inmo.navigation.sample.ui.NavigationModel
import dev.inmo.navigation.sample.ui.NavigationViewConfig
import dev.inmo.navigation.sample.ui.NavigationViewModel
import dev.inmo.micro_utils.startup.plugin.StartPlugin
import dev.inmo.navigation.compose.InjectNavigationChain
import dev.inmo.navigation.compose.InjectNavigationNode
import dev.inmo.navigation.compose.nodeFactory
import dev.inmo.navigation.compose.*
import dev.inmo.navigation.core.NavigationChain
import dev.inmo.navigation.core.NavigationNode
import dev.inmo.navigation.core.NavigationNodeFactory
import dev.inmo.navigation.core.configs.NavigationNodeDefaultConfig
import dev.inmo.navigation.core.extensions.changesInSubtreeFlow
import dev.inmo.navigation.sample.ui.NavigationView
import dev.inmo.navigation.sample.ui.tree.CurrentTreeViewConfig
import dev.inmo.navigation.sample.ui.tree.CurrentTreeViewViewModel
Expand Down Expand Up @@ -87,6 +88,12 @@ object CommonPlugin : StartPlugin {
nodesFactory = koin.nodeFactory(),
dropRedundantChainsOnRestore = true,
) {
val rootChain = getChainFromLocalProvider<NavigationNodeDefaultConfig>()!!
LaunchedEffect(rootChain) {
rootChain.either<NavigationChain<NavigationNodeDefaultConfig>, NavigationNode<out NavigationNodeDefaultConfig, NavigationNodeDefaultConfig>>().changesInSubtreeFlow().collect {
println(it)
}
}
InjectNavigationChain<NavigationNodeDefaultConfig> {
InjectNavigationNode(
NavigationViewConfig(id = "root", text = ">")
Expand Down
14 changes: 3 additions & 11 deletions sample/src/commonMain/kotlin/ui/tree/CurrentTreeViewViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import dev.inmo.kslog.common.logger
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.navigation.core.NavigationNode
import dev.inmo.navigation.core.configs.NavigationNodeDefaultConfig
import dev.inmo.navigation.core.extensions.changesInSubTreeFlow
import dev.inmo.navigation.core.extensions.onChangesInSubTree
import dev.inmo.navigation.core.extensions.rootChain
import dev.inmo.navigation.core.visiter.walkOnNodes
Expand All @@ -24,17 +25,8 @@ class CurrentTreeViewViewModel(
val mermaidLines = mutableStateOf(emptyList<String>())
private var listeningJob: Job? = null
private val listeningJobsMutex = Mutex()
private val changesListeningJob = node.chain.rootChain().onChangesInSubTree(scope) { _, _ ->
listeningJobsMutex.withLock {
val flows = mutableListOf<Flow<Any>>(flowOf(Unit))
node.chain.rootChain().walkOnNodes {
flows.add(it.statesFlow)
}
listeningJob ?.cancel()
listeningJob = flows.merge().subscribeSafelyWithoutExceptions(scope) {
updateFun()
}
}
private val changesListeningJob = node.chain.rootChain().changesInSubTreeFlow().subscribeSafelyWithoutExceptions(scope) {
updateFun()
}


Expand Down

0 comments on commit e50488c

Please sign in to comment.