Skip to content

Commit

Permalink
Create UI states (#41)
Browse files Browse the repository at this point in the history
* SettingsUiState.kt

* create ButtonUiState.kt

* use reviewButtonUiState on ManageStockViewModel.kt

* Remove toolbar title property

* Remove local properties on BackdropComponent.kt

* ktlintCheck

* collect by viewmodels

* ktlintCheck

* Add transaction date to SettingsUiState.kt

* collect settingsUiState in MainContent.kt

* ktlintCheck

* Mobe fromLabel and deliver label to SettingsUiState.kt

* Move fromLabel and deliver label to SettingsUiState.kt

* Remove hardcoded programUid

* remove items from cache when cleared

* remove activity_manage_stock.xml

Co-authored-by: andresmr <[email protected]>
  • Loading branch information
andresmr and andresmr committed Feb 8, 2023
1 parent 8216de7 commit ea52015
Show file tree
Hide file tree
Showing 17 changed files with 233 additions and 1,093 deletions.
2 changes: 1 addition & 1 deletion app/src/main/assets/paperwork.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"buildTime":"2022-12-08 10:37","gitSha":"7a66f01fe"}
{"buildTime":"2023-01-09 10:19","gitSha":"6885dc400"}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ class HomeActivity : AppCompatActivity(), OnOrgUnitSelectionFinished {
?.let { manageStockViewModel.setConfig(it) }

setContent {
updateTheme(viewModel.transactionType.collectAsState().value)
val settingsUiState by viewModel.settingsUiState.collectAsState()
updateTheme(settingsUiState.transactionType)
MdcTheme {
Surface(
modifier = Modifier.fillMaxSize(),
Expand All @@ -77,14 +78,13 @@ class HomeActivity : AppCompatActivity(), OnOrgUnitSelectionFinished {
manageStockViewModel,
Color(colorResource(themeColor).toArgb()),
supportFragmentManager,
this@HomeActivity,
barcodeLauncher,
::navigateToReviewStock
) { scope, scaffold ->
synchronizeData(
scope,
scaffold,
viewModel.config.program
settingsUiState.programUid
)
}
}
Expand Down Expand Up @@ -198,8 +198,6 @@ class HomeActivity : AppCompatActivity(), OnOrgUnitSelectionFinished {

override fun onSelectionFinished(selectedOrgUnits: List<OrganisationUnit>) {
viewModel.setFacility(selectedOrgUnits[0])
viewModel.fromFacilitiesLabel(selectedOrgUnits[0].displayName().toString())
viewModel.setSelectedText(selectedOrgUnits[0].displayName().toString())
setOrgUnitFilters(selectedOrgUnits)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,11 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.SavedStateHandle
import dagger.hilt.android.lifecycle.HiltViewModel
import io.reactivex.disposables.CompositeDisposable
import java.time.Instant
import java.time.LocalDateTime
import java.time.ZoneId
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import org.dhis2.android.rtsm.R
import org.dhis2.android.rtsm.commons.Constants.INTENT_EXTRA_APP_CONFIG
import org.dhis2.android.rtsm.data.AppConfig
Expand All @@ -24,8 +22,8 @@ import org.dhis2.android.rtsm.services.MetadataManager
import org.dhis2.android.rtsm.services.preferences.PreferenceProvider
import org.dhis2.android.rtsm.services.scheduler.BaseSchedulerProvider
import org.dhis2.android.rtsm.ui.base.BaseViewModel
import org.dhis2.android.rtsm.ui.home.model.SettingsUiState
import org.dhis2.android.rtsm.utils.ParcelUtils
import org.dhis2.android.rtsm.utils.UIText
import org.dhis2.android.rtsm.utils.humanReadableDate
import org.hisp.dhis.android.core.option.Option
import org.hisp.dhis.android.core.organisationunit.OrganisationUnit
Expand All @@ -39,42 +37,13 @@ class HomeViewModel @Inject constructor(
savedState: SavedStateHandle
) : BaseViewModel(preferenceProvider, schedulerProvider) {

val config: AppConfig = savedState.get<AppConfig>(INTENT_EXTRA_APP_CONFIG)
private val config: AppConfig = savedState.get<AppConfig>(INTENT_EXTRA_APP_CONFIG)
?: throw InitializationException("Some configuration parameters are missing")

private val _transactionType = MutableStateFlow(TransactionType.DISTRIBUTION)
val transactionType: StateFlow<TransactionType> get() = _transactionType

private val _isDistribution = MutableStateFlow(true)
val isDistribution: StateFlow<Boolean>
get() = _isDistribution

private val _hasFacilitySelected = MutableStateFlow(false)
val hasFacilitySelected: StateFlow<Boolean>
get() = _hasFacilitySelected

private val _hasDestinationSelected = MutableStateFlow(false)
val hasDestinationSelected: StateFlow<Boolean>
get() = _hasDestinationSelected

private val _facility = MutableStateFlow<OrganisationUnit?>(null)
val facility: StateFlow<OrganisationUnit?>
get() = _facility

private val _scanText = MutableStateFlow("")
val scanText = _scanText.asStateFlow()

private val _oldSelectedFacility = MutableStateFlow("")
val oldSelectedFacility = _oldSelectedFacility.asStateFlow()

private val _transactionDate = MutableStateFlow(LocalDateTime.now())
val transactionDate: StateFlow<LocalDateTime>
get() = _transactionDate

private val _destination = MutableStateFlow<Option?>(null)
val destination: StateFlow<Option?>
get() = _destination

// TODO("IS this duplicated: facilities and org units list")
private val _facilities =
MutableStateFlow<OperationState<List<OrganisationUnit>>>(OperationState.Loading)
val facilities: StateFlow<OperationState<List<OrganisationUnit>>>
Expand All @@ -90,18 +59,8 @@ class HomeViewModel @Inject constructor(
val destinationsList: StateFlow<OperationState<List<Option>>>
get() = _destinations

// Toolbar section variables
private val _toolbarTitle = MutableStateFlow(TransactionType.DISTRIBUTION)
val toolbarTitle: StateFlow<TransactionType> get() = _toolbarTitle

private val _fromFacility = MutableStateFlow(UIText.StringRes(R.string.from_facility))
val fromFacility: StateFlow<UIText> get() = _fromFacility

private val _deliveryTo = MutableStateFlow<UIText?>(UIText.StringRes(R.string.to_facility))
val deliveryTo: StateFlow<UIText?> get() = _deliveryTo

private val _orgUnitName = MutableStateFlow("")
val orgUnitName: StateFlow<String> get() = _orgUnitName
private val _settingsUiSate = MutableStateFlow(SettingsUiState(programUid = config.program))
val settingsUiState: StateFlow<SettingsUiState> = _settingsUiSate

init {
loadFacilities()
Expand Down Expand Up @@ -135,7 +94,11 @@ class HomeViewModel @Inject constructor(
_facilities.value = (OperationState.Success(it))
_orgUnitList.value = it

if (it.size == 1) _facility.value = (it[0])
if (it.size == 1) {
_settingsUiSate.update { currentUiState ->
currentUiState.copy(facility = it[0])
}
}
},
{
it.printStackTrace()
Expand All @@ -146,40 +109,42 @@ class HomeViewModel @Inject constructor(
}

fun selectTransaction(type: TransactionType) {
_transactionType.value = type
_isDistribution.value = type == TransactionType.DISTRIBUTION
_settingsUiSate.update { currentUiState ->
currentUiState.copy(transactionType = type)
}

// Distributed to cannot only be set for DISTRIBUTION,
// so ensure you clear it for others if it has been set
if (type != TransactionType.DISTRIBUTION) {
_destination.value = null
_deliveryTo.value = null
} else {
_deliveryTo.value = UIText.StringRes(R.string.to_facility)
_settingsUiSate.update { currentUiState ->
currentUiState.copy(destination = null)
}
}
}

fun setFacility(facility: OrganisationUnit) {
_facility.value = facility
_settingsUiSate.update { currentUiState ->
currentUiState.copy(facility = facility)
}
}

fun setDestination(destination: Option?) {
if (!isDistribution.value) {
if (settingsUiState.value.transactionType != TransactionType.DISTRIBUTION) {
throw UnsupportedOperationException(
"Cannot set 'distributed to' for non-distribution transactions"
)
}

_destination.value = destination
_settingsUiSate.update { currentUiState ->
currentUiState.copy(destination = destination)
}
}

fun checkForFieldErrors(): Int? {
return if (_facility.value == null) {
return if (settingsUiState.value.facility == null) {
R.string.mandatory_facility_selection
} else if (_transactionDate.value == null) {
R.string.mandatory_transaction_date_selection
} else if (_transactionType.value == TransactionType.DISTRIBUTION &&
_destination.value == null
} else if (settingsUiState.value.transactionType == TransactionType.DISTRIBUTION &&
settingsUiState.value.destination == null
) {
R.string.mandatory_distributed_to_selection
} else {
Expand All @@ -188,66 +153,25 @@ class HomeViewModel @Inject constructor(
}

fun getData(): Transaction {
if (facility.value == null) {
if (settingsUiState.value.facility == null) {
throw UserIntentParcelCreationException(
"Unable to create parcel with empty facility"
)
}

return Transaction(
transactionType.value,
ParcelUtils.facilityToIdentifiableModelParcel(facility.value!!),
transactionDate.value.humanReadableDate(),
destination.value?.let { ParcelUtils.distributedTo_ToIdentifiableModelParcel(it) }
)
}

fun setTransactionDate(epoch: Long) {
_transactionDate.value = Instant.ofEpochMilli(epoch)
.atZone(ZoneId.systemDefault())
.toLocalDateTime()
}

fun setToolbarTitle(transactionType: TransactionType) {
_toolbarTitle.value = transactionType
}

fun fromFacilitiesLabel(from: String) {
when (transactionType.value) {
TransactionType.DISTRIBUTION -> {
_fromFacility.value = UIText.StringRes(R.string.subtitle, from)
}
TransactionType.DISCARD -> {
_fromFacility.value = UIText.StringRes(R.string.subtitle, from)
}
TransactionType.CORRECTION -> {
_fromFacility.value = UIText.StringRes(R.string.subtitle, from)
settingsUiState.value.transactionType,
ParcelUtils.facilityToIdentifiableModelParcel(settingsUiState.value.facility!!),
settingsUiState.value.transactionDate.humanReadableDate(),
settingsUiState.value.destination?.let {
ParcelUtils.distributedTo_ToIdentifiableModelParcel(
it
)
}
}
}

fun deliveryToLabel(to: String) {
if (transactionType.value == TransactionType.DISTRIBUTION) {
_deliveryTo.value = UIText.StringRes(R.string.subtitle, to)
}
}

fun setSelectedText(text: String) {
_orgUnitName.value = text
)
}

fun setScannedText(text: String) {
_scanText.value = text
}

fun setFacilitySelected(status: Boolean) {
_hasFacilitySelected.value = status
}

fun setDestinationSelected(status: Boolean) {
_hasDestinationSelected.value = status
}
fun setOldSelectedFacility(text: String) {
_oldSelectedFacility.value = text
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.dhis2.android.rtsm.ui.home.model

import org.dhis2.android.rtsm.R
import org.dhis2.android.rtsm.ui.home.model.ButtonVisibilityState.HIDDEN

data class ButtonUiState(
val text: Int = R.string.review,
val icon: Int = R.drawable.proceed_icon,
val visibility: ButtonVisibilityState = HIDDEN
)

enum class ButtonVisibilityState {
HIDDEN,
DISABLED,
ENABLED
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.dhis2.android.rtsm.ui.home.model

import java.time.LocalDateTime
import org.dhis2.android.rtsm.R
import org.dhis2.android.rtsm.data.TransactionType
import org.dhis2.android.rtsm.data.TransactionType.CORRECTION
import org.dhis2.android.rtsm.data.TransactionType.DISCARD
import org.dhis2.android.rtsm.data.TransactionType.DISTRIBUTION
import org.dhis2.android.rtsm.utils.UIText
import org.hisp.dhis.android.core.option.Option
import org.hisp.dhis.android.core.organisationunit.OrganisationUnit

data class SettingsUiState(
val programUid: String,
val transactionType: TransactionType = DISTRIBUTION,
val facility: OrganisationUnit? = null,
val destination: Option? = null,
val transactionDate: LocalDateTime = LocalDateTime.now()
) {
fun hasFacilitySelected() = facility != null
fun hasDestinationSelected() = destination != null
fun fromFacilitiesLabel(): UIText = facility?.let {
val orgUnitName = it.displayName().toString()
return when (transactionType) {
DISTRIBUTION -> {
UIText.StringRes(R.string.subtitle, orgUnitName)
}
DISCARD -> {
UIText.StringRes(R.string.subtitle, orgUnitName)
}
CORRECTION -> {
UIText.StringRes(R.string.subtitle, orgUnitName)
}
}
} ?: UIText.StringRes(R.string.from_facility)

fun deliverToLabel(): UIText? = when (transactionType) {
DISTRIBUTION -> destination?.let {
UIText.StringRes(R.string.subtitle, it.displayName().toString())
} ?: UIText.StringRes(R.string.to_facility)
else -> null
}

fun facilityName() = facility?.let {
it.displayName().toString()
} ?: ""
}
Loading

0 comments on commit ea52015

Please sign in to comment.