Skip to content

Commit

Permalink
fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
InsanusMokrassar committed Nov 30, 2023
1 parent 5fbd3ef commit 40e2b96
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 52 deletions.
15 changes: 12 additions & 3 deletions core/src/commonMain/kotlin/NavigationNode.kt
Original file line number Diff line number Diff line change
Expand Up @@ -119,22 +119,31 @@ abstract class NavigationNode<Config : Base, Base>(
val chainToJob = mutableMapOf<NavigationChain<Base>, Job>()
val chainToJobMutex = Mutex()

(onChainAddedFlow + onChainReplacedFlow.map { it.map { it.second } }).flatten().subscribeSafelyWithoutExceptions(subscope) {
val initialStateOfSubchains = subchains

merge(
onChainAddedFlow(initialStateOfSubchains),
onChainReplacedFlow(initialStateOfSubchains).map {
it.map { it.second }
}
).flatten().subscribeSafelyWithoutExceptions(subscope) {
chainToJobMutex.withLock {
log.d { "Starting ${it.value}" }
chainToJob[it.value] = it.value.start(subscope)
log.d { "Started ${it.value}" }
}
}
(onChainRemovedFlow + onChainReplacedFlow.map { it.map { it.first } }).flatten().subscribeSafelyWithoutExceptions(subscope) {
(onChainRemovedFlow(initialStateOfSubchains) + onChainReplacedFlow(initialStateOfSubchains).map {
it.map { it.first }
}).flatten().subscribeSafelyWithoutExceptions(subscope) {
chainToJobMutex.withLock {
log.d { "Cancelling and removing ${it.value}" }
chainToJob.remove(it.value) ?.cancel()
log.d { "Cancelled and removed ${it.value}" }
}
}

onChainsStackDiffFlow.subscribeSafelyWithoutExceptions(subscope) {
onChainsStackDiffFlow(initialStateOfSubchains).subscribeSafelyWithoutExceptions(subscope) {
log.d { it }
}

Expand Down
43 changes: 30 additions & 13 deletions core/src/commonMain/kotlin/extensions/Chain.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,41 @@ import dev.inmo.micro_utils.common.mapOnSecond
import dev.inmo.navigation.core.*
import kotlinx.coroutines.flow.*

val <Base> NavigationChain<Base>.onNodesStackDiffFlow: Flow<Diff<NavigationNode<out Base, Base>>>
get() {
var previous = stack.toList()
return flow {
stackFlow.collect {
val newValue = stack
val diff = previous.diff(newValue, strictComparison = true)
emit(diff)
previous = newValue.toList()
}
/**
* Start listening the nodes list changes. Result [Flow] is **cold** flow, so, you should be noticed
* that the first state of this [Flow] is equal to [initial] OR (if null) the state of receiver
* [NavigationChain.stack]
*
* If you want to receive info about changes including **current** state of
* [NavigationChain.stack], you should pass [emptyList] into [initial] argument
*
* If you want in result [Flow] to always start from the creation time stack, pass
* [NavigationChain.stack] of receiver as [initial] argument
*/
fun <Base> NavigationChain<Base>.onNodesStackDiffFlow(
initial: List<NavigationNode<out Base, Base>>? = null
): Flow<Diff<NavigationNode<out Base, Base>>> {
return flow {
var previous: List<NavigationNode<out Base, Base>> = initial ?: stack.toList()
stackFlow.collect {
val newValue = stack
val diff = previous.diff(newValue, strictComparison = true)
emit(diff)
previous = newValue.toList()
}
}
}
val <Base> NavigationChain<Base>.onNodesStackDiffFlow: Flow<Diff<NavigationNode<out Base, Base>>>
get() = onNodesStackDiffFlow()
fun <Base> NavigationChain<Base>.onNodeAddedFlow(initial: List<NavigationNode<out Base, Base>>? = null) = onNodesStackDiffFlow(initial).map { it.added }.filter { it.isNotEmpty() }
val <Base> NavigationChain<Base>.onNodeAddedFlow
get() = onNodesStackDiffFlow.map { it.added }.filter { it.isNotEmpty() }
get() = onNodeAddedFlow()
fun <Base> NavigationChain<Base>.onNodeRemovedFlow(initial: List<NavigationNode<out Base, Base>>? = null) = onNodesStackDiffFlow(initial).map { it.removed }.filter { it.isNotEmpty() }
val <Base> NavigationChain<Base>.onNodeRemovedFlow
get() = onNodesStackDiffFlow.map { it.removed }.filter { it.isNotEmpty() }
get() = onNodeRemovedFlow()
fun <Base> NavigationChain<Base>.onNodeReplacedFlow(initial: List<NavigationNode<out Base, Base>>? = null) = onNodesStackDiffFlow(initial).map { it.replaced }.filter { it.isNotEmpty() }
val <Base> NavigationChain<Base>.onNodeReplacedFlow
get() = onNodesStackDiffFlow.map { it.replaced }.filter { it.isNotEmpty() }
get() = onNodeReplacedFlow()

fun <Base> NavigationChain<Base>.rootChain(): NavigationChain<Base> = parentNode ?.chain ?.rootChain() ?: this

Expand Down
45 changes: 34 additions & 11 deletions core/src/commonMain/kotlin/extensions/Node.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,44 @@ import dev.inmo.micro_utils.common.mapOnSecond
import dev.inmo.navigation.core.*
import kotlinx.coroutines.flow.*

val <Base> NavigationNode<out Base, Base>.onChainsStackDiffFlow: Flow<Diff<NavigationChain<Base>>>
get() = flow {
var previous = emptyList<NavigationChain<Base>>()
subchainsFlow.collect {
val newValue = subchainsFlow.value
emit(previous.diff(newValue, strictComparison = true))
previous = newValue
}
/**
* Start listening the stack changes. Result [Flow] is **cold** flow, so, you should be noticed
* that the first state of this [Flow] is equal to [initial] OR (if null) the state of receiver
* [NavigationNode.subchains]
*
* If you want to receive info about changes including **current** state of
* [NavigationNode.subchains], you should pass [emptyList] into [initial] argument
*
* If you want result [Flow] to always start from the creation time subchains list, pass
* [NavigationNode.subchains] of receiver as [initial] argument
*/
fun <Base> NavigationNode<out Base, Base>.onChainsStackDiffFlow(
initial: List<NavigationChain<Base>>? = null
): Flow<Diff<NavigationChain<Base>>> = flow {
var previous: List<NavigationChain<Base>> = initial ?: subchains
subchainsFlow.collect {
val newValue = subchains
emit(previous.diff(newValue, strictComparison = true))
previous = newValue
}
}
val <Base> NavigationNode<out Base, Base>.onChainsStackDiffFlow: Flow<Diff<NavigationChain<Base>>>
get() = onChainsStackDiffFlow()
fun <Base> NavigationNode<out Base, Base>.onChainAddedFlow(
initial: List<NavigationChain<Base>>? = null
) = onChainsStackDiffFlow(initial).map { it.added }.filter { it.isNotEmpty() }
val <Base> NavigationNode<out Base, Base>.onChainAddedFlow
get() = onChainsStackDiffFlow.map { it.added }.filter { it.isNotEmpty() }
get() = onChainAddedFlow()
fun <Base> NavigationNode<out Base, Base>.onChainRemovedFlow(
initial: List<NavigationChain<Base>>? = null
) = onChainsStackDiffFlow(initial).map { it.removed }.filter { it.isNotEmpty() }
val <Base> NavigationNode<out Base, Base>.onChainRemovedFlow
get() = onChainsStackDiffFlow.map { it.removed }.filter { it.isNotEmpty() }
get() = onChainRemovedFlow()
fun <Base> NavigationNode<out Base, Base>.onChainReplacedFlow(
initial: List<NavigationChain<Base>>? = null
) = onChainsStackDiffFlow(initial).map { it.replaced }.filter { it.isNotEmpty() }
val <Base> NavigationNode<out Base, Base>.onChainReplacedFlow
get() = onChainsStackDiffFlow.map { it.replaced }.filter { it.isNotEmpty() }
get() = onChainReplacedFlow()

fun <Base> NavigationNode<*, Base>.findNode(id: NavigationNodeId): NavigationNode<*, Base>? = if (this.id == id) {
this
Expand Down
58 changes: 35 additions & 23 deletions core/src/commonMain/kotlin/repo/HierarchyRepoUpdater.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,34 +33,46 @@ fun <T> NavigationConfigsRepo<T>.enableSavingHierarchy(

fun NavigationChain<T>.enableListeningUpdates(scope: CoroutineScope) {
val currentSubscope = scope.LinkedSupervisorScope()
onNodesStackDiffFlow.filter { it.isEmpty() }.subscribeSafelyWithoutExceptions(currentSubscope) {
var needSave = false
needSave = needSave || it.added.any { it.value.storableInNavigationHierarchy }
needSave = needSave || it.replaced.any { it.first.value.storableInNavigationHierarchy || it.second.value.storableInNavigationHierarchy }
needSave = needSave || it.removed.any { it.value.storableInNavigationHierarchy }
if (needSave) {
save("initialization")
onNodesStackDiffFlow(stack).subscribeSafelyWithoutExceptions(currentSubscope) {
val eventsForSave = mutableSetOf<String>()
it.added.forEach { (_, newNode) ->
runCatching {
val mustBeSaved = newNode.storableInNavigationHierarchy
if (mustBeSaved) {
eventsForSave.add("node adding")
newNode.enableListeningUpdates(currentSubscope)
newNode.onChainAddedFlow(emptyList()).subscribeSafelyWithoutExceptions(scope) { newChains ->
save("chain adding")
newChains.forEach { newChain ->
newChain.value.enableListeningUpdates(scope)
}
}
newNode.onChainRemovedFlow(emptyList()).subscribeSafelyWithoutExceptions(scope) {
save("chain removing")
}
}
}
}
}
onNodeAddedFlow.flatten().subscribeSafelyWithoutExceptions(currentSubscope) { (_, newNode) ->
val mustBeSaved = newNode.storableInNavigationHierarchy
if (mustBeSaved) {
save("node adding")
newNode.enableListeningUpdates(currentSubscope)
newNode.onChainAddedFlow.subscribeSafelyWithoutExceptions(scope) { newChains ->
save("chain adding")
newChains.forEach { newChain ->
newChain.value.enableListeningUpdates(scope)
it.removed.forEach { (i, node) ->
runCatching {
if (node.storableInNavigationHierarchy) {
eventsForSave.add("node removing")
}
}
newNode.onChainRemovedFlow.subscribeSafelyWithoutExceptions(scope) {
save("chain removing")
}
runCatching {
var needSave = false
needSave = needSave || it.added.any { it.value.storableInNavigationHierarchy }
needSave = needSave || it.replaced.any { it.first.value.storableInNavigationHierarchy || it.second.value.storableInNavigationHierarchy }
needSave = needSave || it.removed.any { it.value.storableInNavigationHierarchy }
if (needSave) {
eventsForSave.add("initialization")
}
}
}
onNodeRemovedFlow.flatten().subscribeSafelyWithoutExceptions(currentSubscope) { (i, node) ->
if (node.storableInNavigationHierarchy) {
save("node removing")
runCatching {
if (eventsForSave.isNotEmpty()) {
save(eventsForSave.joinToString("|"))
}
}
}
stack.forEach {
Expand Down
6 changes: 4 additions & 2 deletions sample/src/commonMain/kotlin/ui/NavigationViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ import dev.inmo.navigation.mvvm.ViewModel
import dev.inmo.navigation.core.NavigationNode
import dev.inmo.navigation.core.configs.NavigationNodeDefaultConfig
import dev.inmo.navigation.core.extensions.onChainAddedFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.withContext

class NavigationViewModel (
Expand All @@ -25,9 +27,9 @@ class NavigationViewModel (
node.stateChangesFlow.subscribeSafelyWithoutExceptions(scope) {
logger.i { "Current state change of node $node is ${it.type}" }
}
node.onChainAddedFlow.subscribeSafelyWithoutExceptions(scope) {
node.onChainAddedFlow(emptyList()).map { it.map { it.value } }.subscribeSafelyWithoutExceptions(scope) {
it.forEach {
it.value.stackFlow.subscribeSafelyWithoutExceptions(scope) {
it.stackFlow.subscribeSafelyWithoutExceptions(scope) {
val id = it.firstOrNull() ?.config ?.id ?: return@subscribeSafelyWithoutExceptions
doInUI {
if (id !in _subnodesIds) {
Expand Down

0 comments on commit 40e2b96

Please sign in to comment.