-
Notifications
You must be signed in to change notification settings - Fork 202
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement RealDispatcher and Related Classes (#621)
* Implement RealDispatcher and Related Classes Signed-off-by: mramotar <mramotar@dropbox.com> * Implement RealOptionalInjector Signed-off-by: mramotar <mramotar@dropbox.com> * Implement DefaultLogger Signed-off-by: mramotar <mramotar@dropbox.com> --------- Signed-off-by: mramotar <mramotar@dropbox.com>
- Loading branch information
1 parent
0b901bc
commit c38c694
Showing
12 changed files
with
347 additions
and
0 deletions.
There are no files selected for viewing
5 changes: 5 additions & 0 deletions
5
paging/core/src/commonMain/kotlin/org/mobilenativefoundation/paging/core/Logger.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package org.mobilenativefoundation.paging.core | ||
|
||
interface Logger { | ||
fun log(message: String) | ||
} |
5 changes: 5 additions & 0 deletions
5
paging/core/src/commonMain/kotlin/org/mobilenativefoundation/paging/core/impl/Constants.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package org.mobilenativefoundation.paging.core.impl | ||
|
||
internal object Constants { | ||
const val UNCHECKED_CAST = "UNCHECKED_CAST" | ||
} |
22 changes: 22 additions & 0 deletions
22
...g/core/src/commonMain/kotlin/org/mobilenativefoundation/paging/core/impl/DefaultLogger.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package org.mobilenativefoundation.paging.core.impl | ||
|
||
import co.touchlab.kermit.CommonWriter | ||
import org.mobilenativefoundation.paging.core.Logger | ||
|
||
class DefaultLogger : Logger { | ||
override fun log(message: String) { | ||
logger.d( | ||
""" | ||
$message | ||
""".trimIndent(), | ||
) | ||
} | ||
|
||
private val logger = | ||
co.touchlab.kermit.Logger.apply { | ||
setLogWriters(listOf(CommonWriter())) | ||
setTag("org.mobilenativefoundation.paging") | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
paging/core/src/commonMain/kotlin/org/mobilenativefoundation/paging/core/impl/Dispatcher.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package org.mobilenativefoundation.paging.core.impl | ||
|
||
import org.mobilenativefoundation.paging.core.PagingAction | ||
|
||
/** | ||
* A dispatcher for handling paging actions and dispatching them to the appropriate middleware and reducer. | ||
* | ||
* @param Id The type of the unique identifier for each item in the paged data. | ||
* @param K The type of the key used for paging. | ||
* @param P The type of the parameters associated with each page of data. | ||
* @param D The type of the data items. | ||
* @param E The type of errors that can occur during the paging process. | ||
* @param A The type of custom actions that can be dispatched to modify the paging state. | ||
*/ | ||
interface Dispatcher<Id : Comparable<Id>, K : Any, P : Any, D : Any, E : Any, A : Any> { | ||
|
||
/** | ||
* Dispatches a paging action to the middleware and reducer chain. | ||
* | ||
* @param PA The type of the paging action being dispatched. | ||
* @param action The paging action to dispatch. | ||
* @param index The index of the middleware to start dispatching from. Default is 0. | ||
*/ | ||
fun <PA : PagingAction<Id, K, P, D, E, A>> dispatch(action: PA, index: Int = 0) | ||
} |
79 changes: 79 additions & 0 deletions
79
...g/core/src/commonMain/kotlin/org/mobilenativefoundation/paging/core/impl/EffectsHolder.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package org.mobilenativefoundation.paging.core.impl | ||
|
||
import org.mobilenativefoundation.paging.core.Effect | ||
import org.mobilenativefoundation.paging.core.PagingAction | ||
import org.mobilenativefoundation.paging.core.PagingState | ||
import org.mobilenativefoundation.paging.core.impl.Constants.UNCHECKED_CAST | ||
import kotlin.reflect.KClass | ||
|
||
/** | ||
* A type alias representing a mapping from a [PagingAction] to a list of [Effect]s. | ||
*/ | ||
typealias PagingActionToEffects<Id, K, P, D, E, A> = MutableMap<KClass<out PagingAction<Id, K, P, D, E, A>>, MutableList<Effect<Id, K, P, D, E, A, *, *>>> | ||
|
||
/** | ||
* A type alias representing a mapping from a [PagingState] to a [PagingActionToEffects] map. | ||
*/ | ||
typealias PagingStateToPagingActionToEffects<Id, K, P, D, E, A> = MutableMap<KClass<out PagingState<Id, K, P, D, E>>, PagingActionToEffects<Id, K, P, D, E, A>> | ||
|
||
/** | ||
* A class for holding and managing effects based on [PagingAction] and [PagingState] types. | ||
* | ||
* @param Id The type of the unique identifier for each item in the paged data. | ||
* @param K The type of the key used for paging. | ||
* @param P The type of the parameters associated with each page of data. | ||
* @param D The type of the data items. | ||
* @param E The type of errors that can occur during the paging process. | ||
* @param A The type of custom actions that can be dispatched to modify the paging state. | ||
*/ | ||
@Suppress(UNCHECKED_CAST) | ||
class EffectsHolder<Id : Comparable<Id>, K : Any, P : Any, D : Any, E : Any, A : Any> { | ||
private val effects: PagingStateToPagingActionToEffects<Id, K, P, D, E, A> = mutableMapOf() | ||
|
||
/** | ||
* Retrieves the list of effects associated with the specified [PagingAction] and [PagingState] types. | ||
* | ||
* @param PA The type of the [PagingAction]. | ||
* @param S The type of the [PagingState]. | ||
* @param action The [KClass] of the [PagingAction]. | ||
* @param state The [KClass] of the [PagingState]. | ||
* @return The list of effects associated with the specified [PagingAction] and [PagingState] types. | ||
*/ | ||
fun <PA : PagingAction<Id, K, P, D, E, A>, S : PagingState<Id, K, P, D, E>> get( | ||
action: KClass<out PagingAction<Id, K, P, D, E, A>>, | ||
state: KClass<out PagingState<Id, K, P, D, E>> | ||
): List<Effect<Id, K, P, D, E, A, PA, S>> { | ||
action as KClass<PA> | ||
state as KClass<S> | ||
|
||
return effects[state]?.get(action) as? List<Effect<Id, K, P, D, E, A, PA, S>> ?: emptyList() | ||
} | ||
|
||
/** | ||
* Adds an effect to the list of effects associated with the specified [PagingAction] and [PagingState] types. | ||
* | ||
* @param PA The type of the [PagingAction]. | ||
* @param S The type of the [PagingState]. | ||
* @param action The [KClass] of the [PagingAction]. | ||
* @param state The [KClass] of the [PagingState]. | ||
* @param effect The effect to add. | ||
*/ | ||
fun <PA : PagingAction<Id, K, P, D, E, A>, S : PagingState<Id, K, P, D, E>> put( | ||
action: KClass<out PagingAction<*, *, *, *, *, *>>, | ||
state: KClass<out PagingState<*, *, *, *, *>>, | ||
effect: Effect<Id, K, P, D, E, A, PA, S> | ||
) { | ||
action as KClass<out PA> | ||
state as KClass<out S> | ||
|
||
if (state !in effects) { | ||
effects[state] = mutableMapOf() | ||
} | ||
|
||
if (action !in effects[state]!!) { | ||
effects[state]!![action] = mutableListOf() | ||
} | ||
|
||
effects[state]!![action]!!.add(effect) | ||
} | ||
} |
34 changes: 34 additions & 0 deletions
34
...core/src/commonMain/kotlin/org/mobilenativefoundation/paging/core/impl/EffectsLauncher.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package org.mobilenativefoundation.paging.core.impl | ||
|
||
import org.mobilenativefoundation.paging.core.PagingAction | ||
import org.mobilenativefoundation.paging.core.PagingState | ||
import org.mobilenativefoundation.paging.core.impl.Constants.UNCHECKED_CAST | ||
import kotlin.reflect.KClass | ||
|
||
/** | ||
* A class for launching effects based on dispatched [PagingAction]s and the current [PagingState]. | ||
* | ||
* @param Id The type of the unique identifier for each item in the paged data. | ||
* @param K The type of the key used for paging. | ||
* @param P The type of the parameters associated with each page of data. | ||
* @param D The type of the data items. | ||
* @param E The type of errors that can occur during the paging process. | ||
* @param A The type of custom actions that can be dispatched to modify the paging state. | ||
* @property effectsHolder The [EffectsHolder] instance holding the effects. | ||
*/ | ||
@Suppress(UNCHECKED_CAST) | ||
class EffectsLauncher<Id : Comparable<Id>, K : Any, P : Any, D : Any, E : Any, A : Any>( | ||
private val effectsHolder: EffectsHolder<Id, K, P, D, E, A> | ||
) { | ||
|
||
fun <PA : PagingAction<Id, K, P, D, E, A>, S : PagingState<Id, K, P, D, E>> launch(action: PA, state: S, dispatch: (PagingAction<Id, K, P, D, E, A>) -> Unit) { | ||
|
||
effectsHolder.get<PA, S>(action::class, state::class).forEach { effect -> | ||
effect(action, state, dispatch) | ||
} | ||
|
||
effectsHolder.get<PA, PagingState<Id, K, P, D, E>>(action::class, PagingState::class as KClass<out PagingState<Id, K, P, D, E>>).forEach { effect -> | ||
effect(action, state, dispatch) | ||
} | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
paging/core/src/commonMain/kotlin/org/mobilenativefoundation/paging/core/impl/Injector.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package org.mobilenativefoundation.paging.core.impl | ||
|
||
/** | ||
* An interface representing an injector for providing instances of a specific type. | ||
* | ||
* @param T The type of the instance to be injected. | ||
*/ | ||
interface Injector<T : Any> { | ||
|
||
/** | ||
* Injects an instance of type [T]. | ||
* | ||
* @return The injected instance. | ||
*/ | ||
fun inject(): T | ||
} |
15 changes: 15 additions & 0 deletions
15
...ore/src/commonMain/kotlin/org/mobilenativefoundation/paging/core/impl/OptionalInjector.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package org.mobilenativefoundation.paging.core.impl | ||
|
||
/** | ||
* An interface representing an optional injector for providing instances of a specific type. | ||
* | ||
* @param T The type of the instance to be injected. | ||
*/ | ||
interface OptionalInjector<T : Any> { | ||
/** | ||
* Injects an instance of type [T] if available, or returns null. | ||
* | ||
* @return The injected instance or null if not available. | ||
*/ | ||
fun inject(): T? | ||
} |
78 changes: 78 additions & 0 deletions
78
.../core/src/commonMain/kotlin/org/mobilenativefoundation/paging/core/impl/RealDispatcher.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package org.mobilenativefoundation.paging.core.impl | ||
|
||
import kotlinx.coroutines.CoroutineScope | ||
import kotlinx.coroutines.launch | ||
import org.mobilenativefoundation.paging.core.Middleware | ||
import org.mobilenativefoundation.paging.core.PagingAction | ||
import org.mobilenativefoundation.paging.core.PagingState | ||
import org.mobilenativefoundation.paging.core.Reducer | ||
|
||
/** | ||
* A real implementation of the [Dispatcher] interface for handling paging actions and managing the paging state. | ||
* | ||
* @param Id The type of the unique identifier for each item in the paged data. | ||
* @param K The type of the key used for paging. | ||
* @param P The type of the parameters associated with each page of data. | ||
* @param D The type of the data items. | ||
* @param E The type of errors that can occur during the paging process. | ||
* @param A The type of custom actions that can be dispatched to modify the paging state. | ||
* @property stateManager The [StateManager] instance for managing the paging state. | ||
* @property middleware The list of [Middleware] instances to be applied to the dispatched actions. | ||
* @property reducer The [Reducer] instance for reducing the paging state based on the dispatched actions. | ||
* @property effectsLauncher The [EffectsLauncher] instance for launching effects based on the dispatched actions and the current state. | ||
* @property childScope The [CoroutineScope] in which the dispatcher will operate. | ||
*/ | ||
class RealDispatcher<Id : Comparable<Id>, K : Any, P : Any, D : Any, E : Any, A : Any>( | ||
private val stateManager: StateManager<Id, K, P, D, E>, | ||
private val middleware: List<Middleware<Id, K, P, D, E, A>>, | ||
private val reducer: Reducer<Id, K, P, D, E, A>, | ||
private val effectsLauncher: EffectsLauncher<Id, K, P, D, E, A>, | ||
private val childScope: CoroutineScope, | ||
) : Dispatcher<Id, K, P, D, E, A> { | ||
|
||
/** | ||
* Dispatches a paging action to the middleware and reducer chain. | ||
* | ||
* @param PA The type of the paging action being dispatched. | ||
* @param action The paging action to dispatch. | ||
* @param index The index of the middleware to start dispatching from. | ||
*/ | ||
override fun <PA : PagingAction<Id, K, P, D, E, A>> dispatch(action: PA, index: Int) { | ||
if (index < middleware.size) { | ||
|
||
childScope.launch { | ||
middleware[index].apply(action) { nextAction -> | ||
dispatch(nextAction, index + 1) | ||
} | ||
} | ||
|
||
} else { | ||
childScope.launch { | ||
reduceAndLaunchEffects(action) | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Reduces the paging state based on the dispatched action and launches the corresponding effects. | ||
* | ||
* @param PA The type of the paging action being dispatched. | ||
* @param action The paging action to reduce and launch effects for. | ||
*/ | ||
private suspend fun <PA : PagingAction<Id, K, P, D, E, A>> reduceAndLaunchEffects(action: PA) { | ||
val prevState = stateManager.state.value | ||
val nextState = reducer.reduce(action, prevState) | ||
|
||
stateManager.update(nextState) | ||
|
||
when (nextState) { | ||
is PagingState.Initial -> effectsLauncher.launch(action, nextState, ::dispatch) | ||
is PagingState.Data.Idle -> effectsLauncher.launch(action, nextState, ::dispatch) | ||
is PagingState.Data.ErrorLoadingMore<Id, K, P, D, E, *> -> effectsLauncher.launch(action, nextState, ::dispatch) | ||
is PagingState.Data.LoadingMore -> effectsLauncher.launch(action, nextState, ::dispatch) | ||
is PagingState.Error.Custom -> effectsLauncher.launch(action, nextState, ::dispatch) | ||
is PagingState.Error.Exception -> effectsLauncher.launch(action, nextState, ::dispatch) | ||
is PagingState.Loading -> effectsLauncher.launch(action, nextState, ::dispatch) | ||
} | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
...ng/core/src/commonMain/kotlin/org/mobilenativefoundation/paging/core/impl/RealInjector.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package org.mobilenativefoundation.paging.core.impl | ||
|
||
internal class RealInjector<T : Any> : Injector<T> { | ||
var instance: T? = null | ||
|
||
override fun inject(): T { | ||
return instance!! | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
...src/commonMain/kotlin/org/mobilenativefoundation/paging/core/impl/RealOptionalInjector.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package org.mobilenativefoundation.paging.core.impl | ||
|
||
class RealOptionalInjector<T : Any> : OptionalInjector<T> { | ||
var instance: T? = null | ||
|
||
override fun inject(): T? { | ||
return instance | ||
} | ||
} |
50 changes: 50 additions & 0 deletions
50
...ng/core/src/commonMain/kotlin/org/mobilenativefoundation/paging/core/impl/StateManager.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package org.mobilenativefoundation.paging.core.impl | ||
|
||
import kotlinx.coroutines.flow.MutableStateFlow | ||
import kotlinx.coroutines.flow.asStateFlow | ||
import org.mobilenativefoundation.paging.core.Logger | ||
import org.mobilenativefoundation.paging.core.PagingState | ||
|
||
/** | ||
* A class for managing the state of the paging process. | ||
* | ||
* @param Id The type of the unique identifier for each item in the paged data. | ||
* @param K The type of the key used for paging. | ||
* @param P The type of the parameters associated with each page of data. | ||
* @param D The type of the data items. | ||
* @param E The type of errors that can occur during the paging process. | ||
* @param initialState The initial [PagingState]. | ||
* @param loggerInjector The [OptionalInjector] for providing a [Logger] instance. | ||
*/ | ||
class StateManager<Id : Comparable<Id>, K : Any, P : Any, D : Any, E : Any>( | ||
initialState: PagingState<Id, K, P, D, E>, | ||
loggerInjector: OptionalInjector<Logger> | ||
) { | ||
|
||
private val logger = lazy { loggerInjector.inject() } | ||
|
||
private val _state = MutableStateFlow(initialState) | ||
val state = _state.asStateFlow() | ||
|
||
/** | ||
* Updates the state with the specified [PagingState]. | ||
* | ||
* @param nextState The next [PagingState] to update the state with. | ||
*/ | ||
fun update(nextState: PagingState<Id, K, P, D, E>) { | ||
|
||
log(nextState) | ||
|
||
_state.value = nextState | ||
} | ||
|
||
private fun log(nextState: PagingState<Id, K, P, D, E>) { | ||
logger.value?.log( | ||
""" | ||
Updating state: | ||
Previous state: ${_state.value} | ||
Next state: $nextState | ||
""".trimIndent() | ||
) | ||
} | ||
} |