Skip to content

Commit

Permalink
Migrate alert facility change sheet to use Mobius loop (#4824)
Browse files Browse the repository at this point in the history
  • Loading branch information
msasikanth authored Nov 23, 2023
1 parent 78e8538 commit 8d8de95
Show file tree
Hide file tree
Showing 16 changed files with 452 additions and 64 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Next Release

### Internal

- Migrate alert facility change sheet to use Mobius loop

### Fixes

- Fix app crashing when trying to replacing the last screen during navigation
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
package org.simple.clinic.facility.alertchange

sealed class AlertFacilityChangeEffect
sealed interface AlertFacilityChangeEffect {
data object LoadIsFacilityChangedStatus : AlertFacilityChangeEffect

data object MarkFacilityChangedAsFalse : AlertFacilityChangeEffect
}

sealed interface AlertFacilityChangeViewEffect : AlertFacilityChangeEffect {
data object CloseSheetWithContinuation : AlertFacilityChangeViewEffect
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package org.simple.clinic.facility.alertchange

import com.f2prateek.rx.preferences2.Preference
import com.spotify.mobius.functions.Consumer
import com.spotify.mobius.rx2.RxMobius
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import io.reactivex.ObservableTransformer
import org.simple.clinic.facility.alertchange.AlertFacilityChangeEffect.LoadIsFacilityChangedStatus
import org.simple.clinic.facility.alertchange.AlertFacilityChangeEffect.MarkFacilityChangedAsFalse
import org.simple.clinic.facility.alertchange.AlertFacilityChangeEvent.FacilityChangedMarkedAsFalse
import org.simple.clinic.facility.alertchange.AlertFacilityChangeEvent.IsFacilityChangedStatusLoaded
import org.simple.clinic.util.scheduler.SchedulersProvider
import javax.inject.Named

class AlertFacilityChangeEffectHandler @AssistedInject constructor(
@Named("is_facility_switched")
private val isFacilitySwitchedPreference: Preference<Boolean>,
private val schedulersProvider: SchedulersProvider,
@Assisted private val viewEffectsConsumer: Consumer<AlertFacilityChangeViewEffect>
) {

@AssistedFactory
interface Factory {
fun create(viewEffectsConsumer: Consumer<AlertFacilityChangeViewEffect>) : AlertFacilityChangeEffectHandler
}

fun build(): ObservableTransformer<AlertFacilityChangeEffect, AlertFacilityChangeEvent> {
return RxMobius
.subtypeEffectHandler<AlertFacilityChangeEffect, AlertFacilityChangeEvent>()
.addTransformer(LoadIsFacilityChangedStatus::class.java, loadFacilityChangedStatus())
.addTransformer(MarkFacilityChangedAsFalse::class.java, markFacilityChangedAsFalse())
.addConsumer(AlertFacilityChangeViewEffect::class.java, viewEffectsConsumer::accept)
.build()
}

private fun markFacilityChangedAsFalse(): ObservableTransformer<MarkFacilityChangedAsFalse, AlertFacilityChangeEvent> {
return ObservableTransformer { effects ->
effects
.observeOn(schedulersProvider.io())
.map { isFacilitySwitchedPreference.set(false) }
.map { FacilityChangedMarkedAsFalse }
}
}

private fun loadFacilityChangedStatus(): ObservableTransformer<LoadIsFacilityChangedStatus, AlertFacilityChangeEvent> {
return ObservableTransformer { effects ->
effects
.observeOn(schedulersProvider.io())
.map { IsFacilityChangedStatusLoaded(isFacilitySwitchedPreference.get()) }
}
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
package org.simple.clinic.facility.alertchange

sealed class AlertFacilityChangeEvent
import org.simple.clinic.widgets.UiEvent

sealed interface AlertFacilityChangeEvent : UiEvent {

data class IsFacilityChangedStatusLoaded(val isFacilityChanged: Boolean) : AlertFacilityChangeEvent

data object FacilityChangedMarkedAsFalse : AlertFacilityChangeEvent

data object YesButtonClicked : AlertFacilityChangeEvent {
override val analyticsName: String = "Alert Facility Changed Sheet:Yes Button Clicked"
}

data object FacilityChanged : AlertFacilityChangeEvent
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.simple.clinic.facility.alertchange

import com.spotify.mobius.First
import com.spotify.mobius.Init
import org.simple.clinic.facility.alertchange.AlertFacilityChangeEffect.LoadIsFacilityChangedStatus
import org.simple.clinic.mobius.first

class AlertFacilityChangeInit : Init<AlertFacilityChangeModel, AlertFacilityChangeEffect> {

override fun init(model: AlertFacilityChangeModel): First<AlertFacilityChangeModel, AlertFacilityChangeEffect> {
return first(model, LoadIsFacilityChangedStatus)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,18 @@ import android.os.Parcelable
import kotlinx.parcelize.Parcelize

@Parcelize
class AlertFacilityChangeModel : Parcelable
data class AlertFacilityChangeModel(
val isFacilityChanged: Boolean
) : Parcelable {

companion object {

fun default() = AlertFacilityChangeModel(
isFacilityChanged = false
)
}

fun updateIsFacilityChanged(isFacilityChanged: Boolean) = copy(
isFacilityChanged = isFacilityChanged
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,21 @@ import android.os.Parcelable
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.f2prateek.rx.preferences2.Preference
import com.jakewharton.rxbinding3.view.clicks
import com.spotify.mobius.functions.Consumer
import io.reactivex.Observable
import io.reactivex.rxkotlin.cast
import io.reactivex.subjects.PublishSubject
import kotlinx.parcelize.Parcelize
import org.simple.clinic.R
import org.simple.clinic.ReportAnalyticsEvents
import org.simple.clinic.databinding.SheetAlertFacilityChangeBinding
import org.simple.clinic.di.injector
import org.simple.clinic.facility.alertchange.AlertFacilityChangeEvent.FacilityChanged
import org.simple.clinic.facility.alertchange.AlertFacilityChangeEvent.YesButtonClicked
import org.simple.clinic.facility.alertchange.Continuation.ContinueToActivity
import org.simple.clinic.facility.change.FacilityChangeScreen
import org.simple.clinic.feature.Features
import org.simple.clinic.mobius.ViewRenderer
import org.simple.clinic.navigation.v2.Router
import org.simple.clinic.navigation.v2.ScreenKey
import org.simple.clinic.navigation.v2.ScreenResultBus
Expand All @@ -26,24 +32,18 @@ import org.simple.clinic.util.resolveFloat
import org.simple.clinic.util.setFragmentResultListener
import java.util.Locale
import javax.inject.Inject
import javax.inject.Named

class AlertFacilityChangeSheet :
BaseBottomSheet<
AlertFacilityChangeSheet.Key,
SheetAlertFacilityChangeBinding,
AlertFacilityChangeModel,
AlertFacilityChangeEvent,
AlertFacilityChangeEffect,
Unit>() {
class AlertFacilityChangeSheet : BaseBottomSheet<
AlertFacilityChangeSheet.Key,
SheetAlertFacilityChangeBinding,
AlertFacilityChangeModel,
AlertFacilityChangeEvent,
AlertFacilityChangeEffect,
AlertFacilityChangeViewEffect>(), AlertFacilityChangeUi, UiActions {

@Inject
lateinit var locale: Locale

@Inject
@Named("is_facility_switched")
lateinit var isFacilitySwitchedPreference: Preference<Boolean>

@Inject
lateinit var features: Features

Expand All @@ -53,25 +53,12 @@ class AlertFacilityChangeSheet :
@Inject
lateinit var router: Router

override fun defaultModel() = AlertFacilityChangeModel()

override fun uiRenderer(): ViewRenderer<AlertFacilityChangeModel> {
return object : ViewRenderer<AlertFacilityChangeModel> {
override fun render(model: AlertFacilityChangeModel) {
// Nothing to render here
}
}
}

override fun bindView(inflater: LayoutInflater, container: ViewGroup?) =
SheetAlertFacilityChangeBinding.inflate(inflater, container, false)
@Inject
lateinit var effectHandlerFactory: AlertFacilityChangeEffectHandler.Factory

private val currentFacilityName
get() = screenKey.currentFacilityName

private val continuation
get() = screenKey.continuation

private val rootView
get() = binding.root

Expand All @@ -84,6 +71,32 @@ class AlertFacilityChangeSheet :
private val changeButton
get() = binding.changeButton

private val hotEvents = PublishSubject.create<AlertFacilityChangeEvent>()

override fun defaultModel() = AlertFacilityChangeModel.default()

override fun events(): Observable<AlertFacilityChangeEvent> = Observable.mergeArray(
yesButtonClicks(),
hotEvents
)
.compose(ReportAnalyticsEvents())
.cast()

override fun createInit() = AlertFacilityChangeInit()

override fun createUpdate() = AlertFacilityChangeUpdate()

override fun uiRenderer() = AlertFacilityChangeUiRenderer(this)

override fun createEffectHandler(viewEffectsConsumer: Consumer<AlertFacilityChangeViewEffect>) = effectHandlerFactory
.create(viewEffectsConsumer)
.build()

override fun viewEffectsHandler() = AlertFacilityChangeViewEffectHandler(this)

override fun bindView(inflater: LayoutInflater, container: ViewGroup?) =
SheetAlertFacilityChangeBinding.inflate(inflater, container, false)

override fun onAttach(context: Context) {
super.onAttach(context)
context.injector<Injector>().inject(this)
Expand All @@ -92,7 +105,7 @@ class AlertFacilityChangeSheet :
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setFragmentResultListener(ChangeCurrentFacility) { _, result ->
if (result is Succeeded) proceedToNextScreen()
if (result is Succeeded) hotEvents.onNext(FacilityChanged)
}
}

Expand All @@ -104,61 +117,50 @@ class AlertFacilityChangeSheet :

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
facilityName.text = getString(R.string.alertfacilitychange_facility_name, currentFacilityName)

if (isFacilitySwitchedPreference.get().not()) {
rootView.postDelayed(::closeSheetWithContinuation, 100)
} else {
showDialogUi()

facilityName.text = getString(R.string.alertfacilitychange_facility_name, currentFacilityName)
yesButton.setOnClickListener {
proceedToNextScreen()
}

changeButton.setOnClickListener {
openFacilityChangeScreen()
}
changeButton.setOnClickListener {
openFacilityChangeScreen()
}
}

override fun onDestroyView() {
rootView.removeCallbacks(::closeSheetWithContinuation)
super.onDestroyView()
}

private fun showDialogUi() {
override fun showFacilityChangeAlert() {
val backgroundDimAmount = requireContext().resolveFloat(android.R.attr.backgroundDimAmount)
requireDialog().window!!.setDimAmount(backgroundDimAmount)
binding.root.visibility = View.VISIBLE
rootView.visibility = View.VISIBLE
}

private fun proceedToNextScreen() {
isFacilitySwitchedPreference.set(false)
closeSheetWithContinuation()
override fun hideFacilityChangeAlert() {
rootView.visibility = View.GONE
}

private fun closeSheetWithContinuation() {
when (continuation) {
override fun closeSheetWithContinuation() {
when (val continuation = screenKey.continuation) {
is ContinueToActivity -> {
val (intent, requestCode) = (continuation as ContinueToActivity).run {
val (intent, requestCode) = continuation.run {
intent to requestCode
}
requireActivity().startActivityForResult(intent, requestCode)
router.pop()
}

is Continuation.ContinueToScreen -> {
val screenKey = (continuation as Continuation.ContinueToScreen).screenKey
router.replaceTop(screenKey)
router.replaceTop(continuation.screenKey)
}

is Continuation.ContinueToScreenExpectingResult -> {
val screenKey = (continuation as Continuation.ContinueToScreenExpectingResult).screenKey
val requestType = (continuation as Continuation.ContinueToScreenExpectingResult).requestType
val screenKey = continuation.screenKey
val requestType = continuation.requestType

router.replaceTopExpectingResult(requestType, screenKey)
}
}
}

private fun yesButtonClicks() = yesButton
.clicks()
.map { YesButtonClicked }

private fun openFacilityChangeScreen() {
router.pushExpectingResult(ChangeCurrentFacility, FacilityChangeScreen.Key())
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.simple.clinic.facility.alertchange

interface AlertFacilityChangeUi {
fun showFacilityChangeAlert()
fun hideFacilityChangeAlert()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.simple.clinic.facility.alertchange

import org.simple.clinic.mobius.ViewRenderer

class AlertFacilityChangeUiRenderer(
private val ui: AlertFacilityChangeUi
) : ViewRenderer<AlertFacilityChangeModel> {

override fun render(model: AlertFacilityChangeModel) {
if (model.isFacilityChanged) {
ui.showFacilityChangeAlert()
} else {
ui.hideFacilityChangeAlert()
}
}
}
Loading

0 comments on commit 8d8de95

Please sign in to comment.