Skip to content

Commit

Permalink
Docs update
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeka2K committed Sep 22, 2023
1 parent fbe735e commit 6975b3d
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 43 deletions.
68 changes: 39 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ To install the library in your Android project, follow these steps:
Note that you should replace `version` with the [latest release](https://github.com/JekaK/Redux-MVI-for-Android/releases) version available on JitPack.
## Example
1. Start a Koin DI(it's powered by Koin, so you don't have a lot of choise what DI use). viewModelModule is module provided by sample. You will create it by yourself. Or not if you not using ViewModels at your project. Other modules provided by library and SHOULD BE CONNECTED AS BELOW:
1. Start a Koin DI(it's powered by Koin, so you don't have a lot of choise what DI use). viewModelModule is module provided by sample. You will create it by yourself. Or not if you not using ViewModels at your project. Also setup a ViewState, because a default one is using `Any()` type. Other modules provided by library and SHOULD BE CONNECTED AS BELOW:
```kotlin
class App : Application() {
Expand All @@ -41,7 +41,9 @@ Note that you should replace `version` with the [latest release](https://github.
androidContext(this@App)
modules( *listOfModules, *viewModelModule)
}
}
}
val store: Store<Action, AppState> = get()
store.dispatch(SetupStateAction(ViewState()))
}
```

Expand Down Expand Up @@ -91,40 +93,48 @@ In ```init block``` I adding a state for this screen to state list by pass it to
Also ```mainProps``` give us a Flow of props that contains state mapped to props and can be used in our Activity or Fragment or Composable function navigated by Composable navigation.
```kotlin
fun mainProps() = store.stateFlow()
.getStateUpdatesMapped<MainState, MainProps>(bindingDispatcher) {
it.toProps {
store.dispatch(AddCounterAction())
}
}
fun mainProps(): Flow<MainProps> {
return store.stateFlow()
.takeWhenChangedAsViewState<ViewState, MainState> {
it.mainState
}
.map { mainState ->
MainProps(mainState.counter, this::addCounter)
}
.flowOn(bindingDispatcher)
}
```
3. Create an action for some busines logic. In our case we have a button with counter. When we click on button action is dispatching and redusing a new state. This will trigger a state update via flow and show result to user:
```kotlin
class AddCounterAction : ReducibleAction {
override fun reduce(state: AppState): AppState {
return state.applyForState<MainState> {
it.copy(counter = it.counter + 1)
}
}
}
class AddCounterAction : ReducibleAction {
override fun reduce(state: AppState): AppState {
return state.copy(
viewState = state.viewState.updateViewState<ViewState> {
it.copy(
mainState = it.mainState.copy(
counter = it.mainState.counter + 1
)
)
}
)
}
}
```
4. Then use it in Acivity as ```collectAsState``` function:
```kotlin
val props = viewModel.mainProps().collectAsState(initial = MainProps())
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
CounterView(props.value.counter)
Spacer(modifier = Modifier.height(20.dp))
AddButton {
props.value.addCounterAction()
}
}
val props by viewModel.mainProps().collectAsState(initial = MainProps())
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
CounterView(props.counter)
Spacer(modifier = Modifier.height(20.dp))
AddButton(viewModel::addCounter)
}
```
## Middleware
Expand Down Expand Up @@ -163,9 +173,9 @@ class MainMiddleware : Middleware<Action, Store<Action, AppState>> {
* The function is private because it's only used internally by the MainActivity
*/
private fun dispatchCounterToastAction(store: Store<Action, AppState>) {
val counter = store.getState().findState<MainState>().counter
val counter = store.getState().viewState.asViewState<ViewState>().mainState.counter

store.dispatch(ShowCounterToastAction(counter))
store.dispatch(ShowCounterToastAction(counter))
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,19 @@ package com.krykun.reduxmvi.action

import com.krykun.reduxmvi.global.AppState

/**
* A class representing an action to set up the application's state with a specified view state.
* This action is used to update the [AppState] by replacing its view state with a new one.
*
* @param appViewState The new view state to set up in the application's state.
*/
class SetupStateAction(private val appViewState: Any) : ReducibleAction {
/**
* Reduces the current application state by replacing the existing view state with the provided one.
*
* @param state The current [AppState] to be modified.
* @return The modified [AppState] with the updated view state.
*/
override fun reduce(state: AppState): AppState {
return state.copy(
viewState = appViewState
Expand Down
10 changes: 9 additions & 1 deletion ReduxMVI/src/main/java/com/krykun/reduxmvi/ext/FlowExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,15 @@ import kotlinx.coroutines.flow.map
inline fun <T, R> Flow<T>.takeWhenChanged(crossinline transform: suspend (value: T) -> R): Flow<R> =
map(transform).distinctUntilChanged()


/**
* Transforms a Flow of [AppState] into a Flow of a different type [R] by observing changes in the state
* and extracting the [ViewState] contained in the [AppState]. This function is useful for mapping
* from the overall application state to a specific view state.
*
* @param transform A transformation function that takes a value of type [T] extracted from the [ViewState]
* and produces a value of type [R].
* @return A Flow of [R] representing the transformed view state.
*/
inline fun <T, R> Flow<AppState>.takeWhenChangedAsViewState(crossinline transform: suspend (value: T) -> R): Flow<R> =
this.takeWhenChanged<AppState, T> {
it.viewState.asViewState()
Expand Down
17 changes: 16 additions & 1 deletion ReduxMVI/src/main/java/com/krykun/reduxmvi/ext/StateExt.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
package com.krykun.reduxmvi.ext

/**
* Casts the current object to a specified type [T] and returns it.
* This function is used for type casting when it is known that the object represents
* a view state of type [T].
*
* @return The object casted to the specified type [T].
* @throws ClassCastException if the object cannot be cast to type [T].
*/
fun <T> Any.asViewState(): T {
return this as T
}

/**
* Updates a view state object of type [T] by applying a transformation [block] to it.
* This function is designed for modifying view state objects in a type-safe manner.
*
* @param block A transformation function that takes the current view state of type [T] and returns a modified view state of the same type.
* @return The updated view state of type [T].
*/
inline fun <reified T : Any> Any.updateViewState(block: (T) -> T): T {
return this.asViewState<T>().run(block)
}
}
7 changes: 4 additions & 3 deletions sample/src/main/java/com/krykun/sample/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
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.navigation.MainNavigation
import com.krykun.sample.presentation.MainState
import com.krykun.sample.presentation.MainProps
import com.krykun.sample.presentation.MainViewModel
import com.krykun.sample.theme.ReduxMVITheme
import com.krykun.sample.ui.view.AddButton
Expand All @@ -30,15 +31,15 @@ class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val props = viewModel.mainProps().collectAsState(initial = MainState())
val props by viewModel.mainProps().collectAsState(initial = MainProps())
ReduxMVITheme {
val context = LocalContext.current
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
CounterView(props.value.counter)
CounterView(props.counter)
Spacer(modifier = Modifier.height(20.dp))
AddButton(viewModel::addCounter)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import com.krykun.sample.di.Feature
import com.krykun.sample.navigation.MainNavigation
import com.krykun.sample.state.ViewState
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch

/**
Expand Down Expand Up @@ -54,15 +55,20 @@ class MainViewModel(
}

/**
* `propertyProps()` returns a `Property<Int>` that emits the current value of `MainState.counter`
* whenever it changes
* Constructs a Flow of [MainProps] representing the main properties of the application's user interface.
* This function observes changes in the application's state, extracts the relevant [MainState], and maps it
* to a [MainProps] object with specific properties.
*
* @return A Flow of [MainProps] containing the main properties for the user interface.
*/
fun mainProps(): Flow<MainState> {
return store.stateFlow()
.takeWhenChangedAsViewState<ViewState, MainState> {
it.mainState
}
}
fun mainProps() = store.stateFlow()
.takeWhenChangedAsViewState<ViewState, MainState> {
it.mainState
}
.map {
MainProps(it.counter, this::addCounter)
}
.flowOn(bindingDispatcher)

/**
* It returns a Flow of MainNavigation objects that are emitted whenever the navigation state
Expand Down

0 comments on commit 6975b3d

Please sign in to comment.