Skip to content

Instantly share code, notes, and snippets.

@mobileappconsultant
Created December 2, 2020 09:19
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 mobileappconsultant/bcfe01a98dd31c285b51379f339493f6 to your computer and use it in GitHub Desktop.
Save mobileappconsultant/bcfe01a98dd31c285b51379f339493f6 to your computer and use it in GitHub Desktop.
This is an MVI implementation that has a bug. Onclick in the from the MVIFragment isn't firing off any action.
sealed class MoneyBoxIntent : MviIntent {
data class LoginIntent(val loginRequestBody: LoginRequestBody) : MoneyBoxIntent()
object GetAllInvestorProductsIntent : MoneyBoxIntent()
object MakeOneOffPaymentIntent : MoneyBoxIntent()
}
****************************************************************************************************************************************
package com.android.moneybox.presentation
import androidx.lifecycle.ViewModel
import com.android.moneybox.domain.mvi.moneyboxactions.MoneyBoxAction
import com.android.moneybox.domain.mvi.moneyboxactions.MoneyBoxAction.*
import com.android.moneybox.domain.mvi.moneyboxresult.MoneyBoxActionResult
import com.android.moneybox.domain.mvi.moneyboxresult.MoneyBoxActionResult.LoginResult
import com.android.moneybox.domain.mvi.moneyboxresult.MoneyBoxProcessorWrapper
import com.android.moneybox.domain.mvi.moneyboxviewstates.AuthenticationViewState
import com.android.moneybox.domain.mvi.moneyintents.MoneyBoxIntent
import com.android.moneybox.domain.mvi.mvibase.MviViewModel
import io.reactivex.rxjava3.core.ObservableTransformer
import io.reactivex.rxjava3.functions.BiFunction
import javax.inject.Inject
class MoneyBoxMviViewModel @Inject constructor(private val moneyBoxProcessorWrapper: MoneyBoxProcessorWrapper) :
ViewModel(), MviViewModel<MoneyBoxIntent, AuthenticationViewState> {
private val loginIntentSubject =
io.reactivex.rxjava3.subjects.PublishSubject.create<MoneyBoxIntent>()
private val statesObservable: io.reactivex.rxjava3.core.Observable<AuthenticationViewState> =
compose()
private val intentFilter: ObservableTransformer<MoneyBoxIntent, MoneyBoxIntent>
get() = ObservableTransformer { intents ->
intents.publish { shared ->
io.reactivex.rxjava3.core.Observable.merge(
{
shared.ofType(MoneyBoxIntent.LoginIntent::class.java).take(1)
}
)
}
}
private fun compose(): io.reactivex.rxjava3.core.Observable<AuthenticationViewState> {
return loginIntentSubject.compose(intentFilter)
.map(this::actionFromIntent)
.compose(moneyBoxProcessorWrapper.actionProcessor)
.scan(AuthenticationViewState.idle(), reducer)
.distinctUntilChanged().replay(1).autoConnect()
}
private fun actionFromIntent(intent: MoneyBoxIntent): MoneyBoxAction {
return when (intent) {
is MoneyBoxIntent.LoginIntent -> LoginAction(intent.loginRequestBody)
is MoneyBoxIntent.GetAllInvestorProductsIntent -> GetAllInvestorProductsAction
is MoneyBoxIntent.MakeOneOffPaymentIntent -> MakeOneOffPaymentAction
}
}
override fun states(): io.reactivex.rxjava3.core.Observable<AuthenticationViewState> =
statesObservable
override fun processIntents(intents: io.reactivex.rxjava3.core.Observable<MoneyBoxIntent>) {
intents.subscribe(loginIntentSubject)
}
companion object {
private val reducer =
BiFunction { previousState: AuthenticationViewState, result: MoneyBoxActionResult ->
when (result) {
is LoginResult -> {
when (result) {
is LoginResult.Loading -> {
previousState.copy(inProgress = true)
}
is LoginResult.Failure -> {
previousState.copy(inProgress = false, error = result.error)
}
is LoginResult.Success -> {
previousState.copy(
inProgress = false, error = null,
loginResponse = result.moneyBoxProcessorWrapper
)
}
}
}
is MoneyBoxActionResult.GetInvestorProdcutsResult -> {
when (result) {
is MoneyBoxActionResult.GetInvestorProdcutsResult.Loading -> {
previousState.copy(inProgress = true)
}
is MoneyBoxActionResult.GetInvestorProdcutsResult.Failure -> {
previousState.copy(inProgress = false, error = result.error)
}
is MoneyBoxActionResult.GetInvestorProdcutsResult.Success -> {
previousState.copy(
inProgress = false, error = null,
loginResponse = result.moneyBoxProcessorWrapper
)
}
}
}
is MoneyBoxActionResult.OneOfPaymentResult -> {
when (result) {
is MoneyBoxActionResult.OneOfPaymentResult.Loading -> {
previousState.copy(inProgress = true)
}
is MoneyBoxActionResult.OneOfPaymentResult.Failure -> {
previousState.copy(inProgress = false, error = result.error)
}
is MoneyBoxActionResult.OneOfPaymentResult.Success -> {
previousState.copy(
inProgress = false, error = null,
loginResponse = result.moneyBoxProcessorWrapper
)
}
}
}
}
}
}
}
****************************************************************************************************************************************
package com.android.moneybox.domain.mvi.moneyboxresult
import com.android.moneybox.domain.SchedulerProvider
import com.android.moneybox.domain.model.LoginRequestBody
import com.android.moneybox.domain.model.MoneyBoxOneOffPaymentRequest
import com.android.moneybox.domain.mvi.moneyboxactions.MoneyBoxAction
import com.android.moneybox.domain.mvi.moneyboxactions.MoneyBoxAction.GetAllInvestorProductsAction
import com.android.moneybox.domain.mvi.moneyboxactions.MoneyBoxAction.MakeOneOffPaymentAction
import com.android.moneybox.domain.mvi.moneyboxresult.MoneyBoxActionResult.*
import com.android.moneybox.domain.repository.MoenyBoxApiRepository
import io.reactivex.rxjava3.core.ObservableTransformer
import io.reactivex.rxjava3.core.SingleSource
import javax.inject.Inject
class MoneyBoxProcessorWrapper @Inject constructor(
private val repository: MoenyBoxApiRepository,
private val appSchedulerProvider: SchedulerProvider
) {
var loginRequestBody: LoginRequestBody =
LoginRequestBody("jaeren+androidtest@moneyboxapp.com", "P455word12")
lateinit var makeOneOffPaymentRequest: MoneyBoxOneOffPaymentRequest
internal var actionProcessor =
ObservableTransformer<MoneyBoxAction, MoneyBoxActionResult> { actions ->
actions.publish { shared ->
io.reactivex.rxjava3.core.Observable.merge{
shared.ofType(MoneyBoxAction.LoginAction::class.java)
.compose(loginProcessor)
shared.ofType(GetAllInvestorProductsAction::class.java)
.compose(investorProductProcessor)
shared.ofType(MakeOneOffPaymentAction::class.java)
.compose(oneOffPaymentProcessor)
}
}
}
val loginProcessor =
ObservableTransformer<MoneyBoxAction, MoneyBoxActionResult> { actions ->
actions.flatMap {
repository.login(loginRequestBody)
.map { loginResponse -> LoginResult.Success(loginResponse) }
.cast(LoginResult::class.java)
.onErrorReturn(LoginResult::Failure)
.subscribeOn(appSchedulerProvider.io())
.observeOn(appSchedulerProvider.ui())
.startWith(SingleSource { LoginResult.Loading })
}
}
private val investorProductProcessor =
ObservableTransformer<GetAllInvestorProductsAction, GetInvestorProdcutsResult> { actions ->
actions.flatMap {
repository.getInvestorProducts()
.map { investorProducts -> GetInvestorProdcutsResult.Success(investorProducts) }
.cast(GetInvestorProdcutsResult::class.java)
.onErrorReturn(GetInvestorProdcutsResult::Failure)
.subscribeOn(appSchedulerProvider.io())
.observeOn(appSchedulerProvider.ui())
.startWith(SingleSource { GetInvestorProdcutsResult.Loading })
}
}
private val oneOffPaymentProcessor =
ObservableTransformer<MakeOneOffPaymentAction, OneOfPaymentResult> { actions ->
actions.flatMap {
repository.makeOneOffPayment(makeOneOffPaymentRequest)
.map { oneOffPaymentResult -> OneOfPaymentResult.Success(oneOffPaymentResult) }
.cast(OneOfPaymentResult::class.java)
.onErrorReturn(OneOfPaymentResult::Failure)
.subscribeOn(appSchedulerProvider.io())
.observeOn(appSchedulerProvider.ui())
.startWith(SingleSource { OneOfPaymentResult.Loading })
}
}
}
****************************************************************************************************************************************
package com.android.moneybox.presentation
import androidx.lifecycle.ViewModel
import com.android.moneybox.domain.mvi.moneyboxactions.MoneyBoxAction
import com.android.moneybox.domain.mvi.moneyboxactions.MoneyBoxAction.*
import com.android.moneybox.domain.mvi.moneyboxresult.MoneyBoxActionResult
import com.android.moneybox.domain.mvi.moneyboxresult.MoneyBoxActionResult.LoginResult
import com.android.moneybox.domain.mvi.moneyboxresult.MoneyBoxProcessorWrapper
import com.android.moneybox.domain.mvi.moneyboxviewstates.AuthenticationViewState
import com.android.moneybox.domain.mvi.moneyintents.MoneyBoxIntent
import com.android.moneybox.domain.mvi.mvibase.MviViewModel
import io.reactivex.rxjava3.core.ObservableTransformer
import io.reactivex.rxjava3.functions.BiFunction
import javax.inject.Inject
class MoneyBoxMviViewModel @Inject constructor(private val moneyBoxProcessorWrapper: MoneyBoxProcessorWrapper) :
ViewModel(), MviViewModel<MoneyBoxIntent, AuthenticationViewState> {
private val loginIntentSubject =
io.reactivex.rxjava3.subjects.PublishSubject.create<MoneyBoxIntent>()
private val statesObservable: io.reactivex.rxjava3.core.Observable<AuthenticationViewState> =
compose()
private val intentFilter: ObservableTransformer<MoneyBoxIntent, MoneyBoxIntent>
get() = ObservableTransformer { intents ->
intents.publish { shared ->
io.reactivex.rxjava3.core.Observable.merge(
{
shared.ofType(MoneyBoxIntent.LoginIntent::class.java).take(1)
}
)
}
}
private fun compose(): io.reactivex.rxjava3.core.Observable<AuthenticationViewState> {
return loginIntentSubject.compose(intentFilter)
.map(this::actionFromIntent)
.compose(moneyBoxProcessorWrapper.actionProcessor)
.scan(AuthenticationViewState.idle(), reducer)
.distinctUntilChanged().replay(1).autoConnect()
}
private fun actionFromIntent(intent: MoneyBoxIntent): MoneyBoxAction {
return when (intent) {
is MoneyBoxIntent.LoginIntent -> LoginAction(intent.loginRequestBody)
is MoneyBoxIntent.GetAllInvestorProductsIntent -> GetAllInvestorProductsAction
is MoneyBoxIntent.MakeOneOffPaymentIntent -> MakeOneOffPaymentAction
}
}
override fun states(): io.reactivex.rxjava3.core.Observable<AuthenticationViewState> =
statesObservable
override fun processIntents(intents: io.reactivex.rxjava3.core.Observable<MoneyBoxIntent>) {
intents.subscribe(loginIntentSubject)
}
companion object {
private val reducer =
BiFunction { previousState: AuthenticationViewState, result: MoneyBoxActionResult ->
when (result) {
is LoginResult -> {
when (result) {
is LoginResult.Loading -> {
previousState.copy(inProgress = true)
}
is LoginResult.Failure -> {
previousState.copy(inProgress = false, error = result.error)
}
is LoginResult.Success -> {
previousState.copy(
inProgress = false, error = null,
loginResponse = result.moneyBoxProcessorWrapper
)
}
}
}
is MoneyBoxActionResult.GetInvestorProdcutsResult -> {
when (result) {
is MoneyBoxActionResult.GetInvestorProdcutsResult.Loading -> {
previousState.copy(inProgress = true)
}
is MoneyBoxActionResult.GetInvestorProdcutsResult.Failure -> {
previousState.copy(inProgress = false, error = result.error)
}
is MoneyBoxActionResult.GetInvestorProdcutsResult.Success -> {
previousState.copy(
inProgress = false, error = null,
loginResponse = result.moneyBoxProcessorWrapper
)
}
}
}
is MoneyBoxActionResult.OneOfPaymentResult -> {
when (result) {
is MoneyBoxActionResult.OneOfPaymentResult.Loading -> {
previousState.copy(inProgress = true)
}
is MoneyBoxActionResult.OneOfPaymentResult.Failure -> {
previousState.copy(inProgress = false, error = result.error)
}
is MoneyBoxActionResult.OneOfPaymentResult.Success -> {
previousState.copy(
inProgress = false, error = null,
loginResponse = result.moneyBoxProcessorWrapper
)
}
}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment