Skip to content

Instantly share code, notes, and snippets.

@Nimrodda
Created June 11, 2020 20:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Nimrodda/88d20f016278ce1cfcd6dd8d0d0c42f3 to your computer and use it in GitHub Desktop.
Save Nimrodda/88d20f016278ce1cfcd6dd8d0d0c42f3 to your computer and use it in GitHub Desktop.
Redux Kotlin draft
/*
* Copyright 2020 Nimrod Dayan nimroddayan.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.nimroddayan.heartratetracker
import androidx.collection.SparseArrayCompat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.ConflatedBroadcastChannel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
interface Action
typealias AsyncAction<State> = suspend ActionDispatcher<State>.(getState: suspend () -> State) -> Unit
interface Reducer<State> {
fun reduce(previousState: State, action: Action): State
}
const val NO_ID = -1
interface ActionDispatcher<State> {
suspend fun dispatch(action: Action)
fun CoroutineScope.dispatch(action: AsyncAction<State>, actionId: Int = NO_ID)
suspend fun cancelDispatchedAction(actionId: Int)
}
interface ReduxStore<State> : ActionDispatcher<State> {
val stateFlow: Flow<State>
suspend fun getState(): State
}
class Store<State>(
initialState: State,
private val reducer: Reducer<State>
) : ReduxStore<State> {
private var state: State = initialState
private val stateChannel = ConflatedBroadcastChannel(state)
private val actionsInProgress = SparseArrayCompat<Job>()
private val actionsSparseArrayMutex = Mutex()
private val stateMutex = Mutex()
override val stateFlow = stateChannel.asFlow()
override suspend fun getState(): State = stateMutex.withLock { state }
override suspend fun dispatch(action: Action) {
stateMutex.withLock {
state = reducer.reduce(state, action)
stateChannel.offer(state)
}
}
override fun CoroutineScope.dispatch(action: AsyncAction<State>, actionId: Int) {
val job = Job()
launch(job) {
if (actionId != NO_ID) {
actionsSparseArrayMutex.withLock {
actionsInProgress.put(actionId, job)
}
}
action(::getState)
}
}
override suspend fun cancelDispatchedAction(actionId: Int) {
actionsSparseArrayMutex.withLock {
actionsInProgress[actionId]?.cancel()
actionsInProgress.remove(actionId)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment