Skip to content

Commit

Permalink
Created NavigationStateStore
Browse files Browse the repository at this point in the history
  • Loading branch information
chRyNaN committed May 7, 2023
1 parent 20980a5 commit 8135017
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.chrynan.navigation.compose

import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import com.chrynan.navigation.NavigationState
import kotlinx.coroutines.flow.StateFlow

/**
* Converts the [NavigationState.changes] of this [NavigationState] to a Jetpack Compose [State] so that every change
* causes a recomposition of the calling [Composable] function.
*/
@Composable
fun <T> NavigationState<T>.collectAsState(): State<T> =
(this.changes as? StateFlow<T>)?.collectAsState() ?: this.changes.collectAsState(initial = this.initial)
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import kotlinx.serialization.Serializable
* @see [Navigator.navigate]
*/
@Serializable
sealed class NavigationEvent private constructor() {
sealed class NavigationEvent<D : NavigationDestination, C : NavigationContext<D>> private constructor() {

/**
* The [Instant] that the event occurred.
Expand All @@ -35,14 +35,14 @@ sealed class NavigationEvent private constructor() {
*/
@Serializable
@SerialName(value = "back")
class Back internal constructor(
class Back<D : NavigationDestination, C : NavigationContext<D>> internal constructor(
@SerialName(value = "instant") override val instant: Instant = Clock.System.now(),
@SerialName(value = "kind") val kind: Kind
) : NavigationEvent() {
) : NavigationEvent<D, C>() {

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Back) return false
if (other !is Back<*, *>) return false

if (instant != other.instant) return false

Expand Down Expand Up @@ -96,14 +96,14 @@ sealed class NavigationEvent private constructor() {
*/
@Serializable
@SerialName(value = "destination")
class Destination<D : NavigationDestination> internal constructor(
class Destination<D : NavigationDestination, C : NavigationContext<D>> internal constructor(
@SerialName(value = "instant") override val instant: Instant = Clock.System.now(),
@SerialName(value = "destination") val destination: D
) : NavigationEvent() {
) : NavigationEvent<D, C>() {

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Destination<*>) return false
if (other !is Destination<*, *>) return false

if (instant != other.instant) return false

Expand Down Expand Up @@ -131,7 +131,7 @@ sealed class NavigationEvent private constructor() {
class Context<D : NavigationDestination, C : NavigationContext<D>> internal constructor(
@SerialName(value = "instant") override val instant: Instant = Clock.System.now(),
@SerialName(value = "context") val context: C
) : NavigationEvent() {
) : NavigationEvent<D, C>() {

override fun equals(other: Any?): Boolean {
if (this === other) return true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ sealed interface NavigationState<T> {
* purposes of listening to state changes.
*/
val changes: Flow<T>

companion object
}

/**
Expand All @@ -51,12 +53,14 @@ internal sealed interface MutableNavigationState<T> : NavigationState<T> {
* Resets the state back to its [initial] value. This will cause the [initial] value to be emitted to [changes].
*/
fun reset()

companion object
}

/**
* Creates a [MutableNavigationState] instance.
*/
internal fun <T> mutableNavigationStateOf(initial: T): NavigationState<T> =
internal fun <T> mutableNavigationStateOf(initial: T): MutableNavigationState<T> =
StateFlowMutableNavigationState(initial = initial)

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
@file:Suppress("unused")

package com.chrynan.navigation

/**
* Represents a store of navigation state information that is useful for a [Navigator].
*/
interface NavigationStateStore<Destination : NavigationDestination, Context : NavigationContext<Destination>> {

/**
* A [NavigationState] of [NavigationEvent]s.
*/
val event: NavigationState<NavigationEvent<Destination, Context>?>

/**
* A [NavigationState] of [NavigationDestination]s.
*/
val destination: NavigationState<Destination>

/**
* A [NavigationState] of [NavigationContext]s.
*/
val context: NavigationState<Context>
}

/**
* A mutable version of a [NavigationStateStore].
*/
internal interface MutableNavigationStateStore<Destination : NavigationDestination, Context : NavigationContext<Destination>> :
NavigationStateStore<Destination, Context> {

/**
* Dispatches the provided navigation [event] which mutates the underlying state values.
*/
fun dispatch(event: NavigationEvent<Destination, Context>)

/**
* Resets the underlying state values back to their initial values.
*/
fun reset()
}

/**
* Creates a [MutableNavigationStateStore] instance with the provided [initialContext] value.
*/
internal fun <Destination : NavigationDestination, Context : NavigationContext<Destination>> mutableNavigationStateStoreOf(
initialContext: Context
): MutableNavigationStateStore<Destination, Context> =
MapBasedMutableNavigationStateStore(initialContext = initialContext)

/**
* A [MutableNavigationStateStore] implementation that stores [NavigationContext]s and their associated
* [NavigationDestination] [Stack]s in an in-memory [Map].
*/
internal class MapBasedMutableNavigationStateStore<Destination : NavigationDestination, Context : NavigationContext<Destination>> internal constructor(
initialContext: Context
) : MutableNavigationStateStore<Destination, Context> {

override val event = mutableNavigationStateOf<NavigationEvent<Destination, Context>?>(initial = null)
override val destination = mutableNavigationStateOf(initial = initialContext.initialDestination)
override val context = mutableNavigationStateOf(initial = initialContext)

private val map = mutableMapOf(initialContext to mutableStackOf(initialContext.initialDestination))

override fun dispatch(event: NavigationEvent<Destination, Context>) {
this.event.update(event)

TODO("Not yet implemented")
}

override fun reset() {
event.reset()
destination.reset()
context.reset()

map.clear()
map[context.initial] = mutableStackOf(context.initial.initialDestination)
}
}

0 comments on commit 8135017

Please sign in to comment.