Skip to content

Commit

Permalink
Added a sample of middleware implementation and fixed some issues wit…
Browse files Browse the repository at this point in the history
…h Koin connection
  • Loading branch information
JekaK committed Mar 31, 2023
1 parent c8f33a6 commit 65769af
Show file tree
Hide file tree
Showing 12 changed files with 167 additions and 24 deletions.
18 changes: 2 additions & 16 deletions ReduxMVI/src/main/java/com/krykun/reduxmvi/Feature.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,8 @@ package com.krykun.reduxmvi

import com.krykun.reduxmvi.global.Action


enum class Feature {
GLOBAL,
SELF_TEST,
CALL,
ROSTER,
CALL_INVITES,
MEETING_DETAILS,
WAITING_ROOM,
PTZ_CONTROLS,
PRE_CALL,
CHAT,
SAVE_TO_EMR,
CHAT_IMAGE_AUTO_PREVIEW,
PSTN_INVITE,
DIAL_IN,
abstract class Feature {
object GLOBAL : Feature()
}

data class SetupFeature(val feature: Feature) : Action
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ class KoinMiddlewareFactory(private val app: Application) :
MiddlewareFactory<Action, Store<Action, AppState>> {

override fun createForFeature(feature: Feature): List<Middleware<Action, Store<Action, AppState>>> {
return app.get(named(feature.name))
return app.get(named(feature::class.java.simpleName))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ private val middlewareModule = module {
}

private val featuresModule = module {
factory(named(Feature.GLOBAL.name)) {
factory(named(Feature.GLOBAL::class.java.simpleName)) {
listOf(
get<LoggingMiddleware>(),
)
Expand Down
11 changes: 10 additions & 1 deletion sample/src/main/java/com/krykun/sample/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import android.app.Application
import com.krykun.reduxmvi.di.dataModules
import com.krykun.reduxmvi.di.hardwareModules
import com.krykun.reduxmvi.di.presentationModules
import com.krykun.sample.di.featureModule
import com.krykun.sample.di.middlewareModule
import com.krykun.sample.di.viewModelModule
import org.koin.android.ext.koin.androidContext
import org.koin.core.context.startKoin
Expand All @@ -18,7 +20,14 @@ class App : Application() {
private fun setupDIGraph() {
startKoin {
androidContext(this@App)
modules(*hardwareModules, *dataModules, *presentationModules, *viewModelModule)
modules(
*hardwareModules,
*dataModules,
*presentationModules,
*viewModelModule,
*middlewareModule,
*featureModule
)
}
}
}
23 changes: 18 additions & 5 deletions sample/src/main/java/com/krykun/sample/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
package com.krykun.sample

import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.runtime.*
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import com.krykun.sample.presentation.MainProps
import com.krykun.sample.navigation.MainNavigation
import com.krykun.sample.presentation.MainViewModel
import com.krykun.sample.theme.ReduxMVITheme
import com.krykun.sample.ui.view.AddButton
import com.krykun.sample.ui.view.CounterView
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.launch
import org.koin.androidx.viewmodel.ext.android.viewModel

class MainActivity : ComponentActivity() {
Expand All @@ -29,6 +28,7 @@ class MainActivity : ComponentActivity() {
setContent {
ReduxMVITheme {
// A surface container using the 'background' color from the theme
val context = LocalContext.current
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
Expand All @@ -45,6 +45,19 @@ class MainActivity : ComponentActivity() {
}
}
}

LaunchedEffect(key1 = viewModel.navigationEventsState.value) {
when (val event = viewModel.navigationEventsState.value) {
is MainNavigation.ShowCounterToast -> {
Toast.makeText(
context,
"Counter is ${event.counter}",
Toast.LENGTH_SHORT
).show()
}
else -> {}
}
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.krykun.sample.action

import com.krykun.reduxmvi.navigation.action.RequestNavigation
import com.krykun.sample.navigation.MainNavigation

class ShowCounterToastAction(val counter: Int) :
RequestNavigation(MainNavigation.ShowCounterToast(counter))
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.krykun.sample.base

import com.krykun.reduxmvi.navigation.NavigationRequest

15 changes: 15 additions & 0 deletions sample/src/main/java/com/krykun/sample/di/FeatureModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.krykun.sample.di

import com.krykun.sample.middleware.MainMiddleware
import org.koin.core.qualifier.named
import org.koin.dsl.module

sealed class Feature : com.krykun.reduxmvi.Feature() {
object MAIN : Feature()
}

val featureModule = arrayOf(module {
factory(named(Feature.MAIN::class.java.simpleName)) {
listOf(get<MainMiddleware>())
}
})
10 changes: 10 additions & 0 deletions sample/src/main/java/com/krykun/sample/di/MiddlewareModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.krykun.sample.di

import com.krykun.sample.middleware.MainMiddleware
import org.koin.dsl.module

val middlewareModule = arrayOf(module {
factory {
MainMiddleware()
}
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.krykun.sample.middleware

import com.krykun.reduxmvi.ext.findState
import com.krykun.reduxmvi.global.*
import com.krykun.sample.action.AddCounterAction
import com.krykun.sample.action.ShowCounterToastAction
import com.krykun.sample.presentation.MainState

class MainMiddleware : Middleware<Action, Store<Action, AppState>> {

/**
* > When the `AddCounterAction` is dispatched, dispatch a `CounterToastAction`
*
* The `dispatchCounterToastAction` function is defined as follows:
*
* @param action The action that was dispatched.
* @param store The store that the action was dispatched to.
* @param next Dispatcher<Action, Store<Action, AppState>>
* @return The next action
*/
override fun dispatch(
action: Action,
store: Store<Action, AppState>,
next: Dispatcher<Action, Store<Action, AppState>>,
): Action {

val next = next.dispatch(action, store)

when (action) {
is AddCounterAction -> dispatchCounterToastAction(store)
}

return next
}


/**
* "Dispatch a toast action that shows the current counter value."
*
* The function is private because it's only used internally by the MainActivity
*
* @param store Store<Action, AppState> - The store that the middleware is being applied to.
*/
private fun dispatchCounterToastAction(store: Store<Action, AppState>) {
val counter = store.getState().findState<MainState>().counter

store.dispatch(ShowCounterToastAction(counter))
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.krykun.sample.navigation

import com.krykun.reduxmvi.navigation.NavigationRequest

sealed class MainNavigation : NavigationRequest {
object EmptyNavEvent : MainNavigation()
data class ShowCounterToast(val counter: Int) : MainNavigation()
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@ package com.krykun.sample.presentation
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.krykun.reduxmvi.CleanupFeature
import com.krykun.reduxmvi.SetupFeature
import com.krykun.reduxmvi.action.AddStateAction
import com.krykun.reduxmvi.ext.getStateUpdatesMapped
import com.krykun.reduxmvi.ext.getStateUpdatesProperty
import com.krykun.reduxmvi.ext.toDedicatedType
import com.krykun.reduxmvi.global.Action
import com.krykun.reduxmvi.global.AppState
import com.krykun.reduxmvi.global.Store
import com.krykun.reduxmvi.navigation.navigationFlowOf
import com.krykun.sample.action.AddCounterAction
import com.krykun.sample.di.Feature
import com.krykun.sample.navigation.MainNavigation
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.launch

Expand All @@ -27,21 +32,42 @@ class MainViewModel(

var counter = mutableStateOf(0)
val props = mutableStateOf(MainProps())
val navigationEventsState = mutableStateOf<MainNavigation>(MainNavigation.EmptyNavEvent)

/* Creating a new instance of the MainState class and adding it to the store. */
init {
/* Adding the `MainState` to the store and setting up the `MainFeature` */
store.dispatch(AddStateAction(MainState()))
store.dispatch(SetupFeature(Feature.MAIN))
/* Collecting the `Property<Int>` emitted by `propertyProps()` and setting the value of
`counter` to the value emitted by `propertyProps()` */
viewModelScope.launch {
propertyProps()
.collect { value ->
counter.value = value
}
}
/* Collecting the `Flow<MainProps>` emitted by `mainProps()` and setting the value of
`props` to the value emitted by `mainProps()` */
viewModelScope.launch {
mainProps().collect {
props.value = it
}
}
/* Collecting the `Flow<MainNavigation>` emitted by `navigationProps()` and setting the value
of
`navigationEventsState` to the value emitted by `navigationProps()` */
viewModelScope.launch {
navigationProps().collect {
when (it.navigationRequest) {
is MainNavigation.EmptyNavEvent -> {}
else -> {
navigationEventsState.value = it.navigationRequest
}
}
it.consumed()
}
}
}

/**
Expand All @@ -64,4 +90,19 @@ class MainViewModel(
it.counter
}
.toDedicatedType<Int>()


/**
* It returns a Flow of MainNavigation objects that are emitted whenever the navigation state
* changes
*/
private fun navigationProps() = store.navigationFlowOf<MainNavigation>(bindingDispatcher)

/**
* > When the view model is destroyed, dispatch a cleanup action to the store
*/
override fun onCleared() {
store.dispatch(CleanupFeature(Feature.MAIN))
super.onCleared()
}
}

0 comments on commit 65769af

Please sign in to comment.