Need help understanding whether internal state of viewModel is automatically updated along with ProtoDataStore

24 Views Asked by At

Working on an android app (personal project) with Kotlin and Jetpack.

Using Proto datastore to store application settings/configurations. Using Hilt for D.I.

My code was just working fine with intended outcomes, I thought to take "suggestions" from ChatGPT for optimizing my code, and ChatGPT presented me with the refactored version of the code.

** First let me share my code:**

@HiltViewModel
class ApplicationEntryViewModel @Inject constructor(
    private val protoData: DataStore<GeneralApplicationPreferences>
) : ViewModel() {

    private val _applicationStates = MutableStateFlow(GeneralPreferences())
    val applicationStates
        get() = _applicationStates.asStateFlow()


    init {
        viewModelScope.launch {
            protoData.data.collectLatest { preferences: GeneralApplicationPreferences ->

                GLOBAL_DATA_PRECISION = protoDataPrecisionToDataPrecision(preferences.dataPrecision)

                _applicationStates.update { currentValue ->
                    currentValue.copy(
                        currentDestination = preferences.currentDestination.ifEmpty {
                            NavigationDestinations.SAMCompanion.screen_route
                        },

                        showTrailingZero = preferences.showTrailingZero,

                        isHCVisible = preferences.isHcVisible,
                        isWFHClassificationEnabled = preferences.isWfhClassificationVisible,

                        isBMIVisible = preferences.isBmiVisible,
                        isGradeVisible = preferences.isGradeVisible,

                        isICDSDataSamplerEnabled = preferences.isIcdsDataSamplerEnabled,
                        isSchoolDataSamplerEnabled = preferences.isSchoolDataSamplerEnabled
                    )
                }
            }
            SHOW_TRAILING_ZERO = _applicationStates.value.showTrailingZero
        }
    }

    private suspend fun toggleShowICDSDataSampler (isEnabled: Boolean) {
        _applicationStates.update { value ->
            value.copy(
                isICDSDataSamplerEnabled = isEnabled
            )
        }

        viewModelScope.launch {
            protoData.updateData { preferences: GeneralApplicationPreferences ->
                preferences.copy {
                    this.isIcdsDataSamplerEnabled = _applicationStates.value.isICDSDataSamplerEnabled
                }
            }
        }
    }

    private suspend fun toggleShowSchoolDataSampler (isEnabled: Boolean) {
        _applicationStates.update { value ->
            value.copy(
                isSchoolDataSamplerEnabled = isEnabled
            )
        }

        viewModelScope.launch {
            protoData.updateData { preferences: GeneralApplicationPreferences ->
                preferences.copy {
                    this.isSchoolDataSamplerEnabled = _applicationStates.value.isSchoolDataSamplerEnabled
                }
            }
        }
    }

    private suspend fun updateCurrentDestination (destinationRoute: String?) {
        _applicationStates.update { currentValue ->
            currentValue.copy(
                currentDestination = destinationRoute ?: NavigationDestinations.SAMCompanion.screen_route
            )
        }

        viewModelScope.launch {
            protoData.updateData { preferences: GeneralApplicationPreferences ->
                preferences.copy {
                    this.currentDestination = _applicationStates.value.currentDestination
                }
            }
        }
    }

    private suspend fun icdsToggleHCVisibility (isVisible: Boolean) {
        _applicationStates.update { currentValue ->
            currentValue.copy(
                isHCVisible = isVisible
            )
        }

        viewModelScope.launch {
            protoData.updateData { preferences: GeneralApplicationPreferences ->
                preferences.copy {
                    this.isHcVisible = _applicationStates.value.isHCVisible
                }
            }
        }
    }

    private suspend fun icdsToggleWFHClassificationVisibility (isVisible: Boolean) {
        _applicationStates.update { currentValue ->
            currentValue.copy(
                isWFHClassificationEnabled = isVisible
            )
        }

        viewModelScope.launch {
            protoData.updateData { preferences: GeneralApplicationPreferences ->
                preferences.copy {
                    this.isWfhClassificationVisible = _applicationStates.value.isWFHClassificationEnabled
                }
            }
        }
    }

    private suspend fun schoolToggleBMIVisibility (isVisible: Boolean) {
        _applicationStates.update { currentValue ->
            currentValue.copy(
                isBMIVisible = isVisible
            )
        }

        viewModelScope.launch {
            protoData.updateData { preferences: GeneralApplicationPreferences ->
                preferences.copy {
                    this.isBmiVisible = _applicationStates.value.isBMIVisible
                }
            }
        }
    }

    private suspend fun schoolToggleGradeVisibility (isVisible: Boolean) {
        _applicationStates.update { currentValue ->
            currentValue.copy(
                isGradeVisible = isVisible
            )
        }

        viewModelScope.launch {
            protoData.updateData { preferences: GeneralApplicationPreferences ->
                preferences.copy {
                    this.isGradeVisible = _applicationStates.value.isGradeVisible
                }
            }
        }
    }

    private suspend fun updateDataPrecision (newPrecision: DataPrecision) {
        GLOBAL_DATA_PRECISION = newPrecision

        viewModelScope.launch {
            protoData.updateData { preferences: GeneralApplicationPreferences ->
                preferences.copy {
                    this.dataPrecision = GLOBAL_DATA_PRECISION.toProtoDataPrecision()
                }
            }
        }
    }

    private suspend fun toggleShowSchoolHeightInFPS (inFPS: Boolean) {
        _applicationStates.update { value ->
            value.copy(
                showSchoolHeightInFPS = inFPS
            )
        }

        viewModelScope.launch {
            protoData.updateData { preferences: GeneralApplicationPreferences ->
                preferences.copy {
                    this.showSchoolHeightInFps = _applicationStates.value.showSchoolHeightInFPS
                }
            }
        }
    }

    private suspend fun toggleShowTrailingZeroAfterDecimal (showTrailingZero: Boolean) {
        _applicationStates.update { value ->
            value.copy(
                showTrailingZero = showTrailingZero
            )
        }

        SHOW_TRAILING_ZERO = showTrailingZero

        viewModelScope.launch {
            protoData.updateData { preferences: GeneralApplicationPreferences ->
                preferences.copy {
                    this.showTrailingZero = _applicationStates.value.showTrailingZero
                }
            }
        }
    }

    suspend fun handleEventsFor (event: PreferenceEvents) {
        when (event) {
            is PreferenceEvents.DestinationChanged ->           updateCurrentDestination(event.destination)
            is PreferenceEvents.ToggleBMI ->                    schoolToggleBMIVisibility(event.isBMIVisible)
            is PreferenceEvents.ToggleGrade ->                  schoolToggleGradeVisibility(event.isGradeVisible)
            is PreferenceEvents.ToggleHC ->                     icdsToggleHCVisibility(event.isHCVisible)
            is PreferenceEvents.ToggleWFHClassification ->      icdsToggleWFHClassificationVisibility(event.isWFHClassificationEnabled)
            is PreferenceEvents.ToggleShowHeightInFPS ->        toggleShowSchoolHeightInFPS(event.showHeightInFPS)
            is PreferenceEvents.ToggleTrailingZero ->           toggleShowTrailingZeroAfterDecimal(event.showTrailingZero)
            is PreferenceEvents.ToggleGlobalDataPrecision ->    updateDataPrecision(event.precision)
            is PreferenceEvents.ToggleICDSDataSampler ->        toggleShowICDSDataSampler(event.isDataSamplerEnabled)
            is PreferenceEvents.ToggleSchoolDataSampler ->      toggleShowSchoolDataSampler(event.isDataSamplerEnabled)
        }
    }

}

data class GeneralPreferences (
    val currentDestination: String = NavigationDestinations.School.screen_route,

    val isICDSDataSamplerEnabled: Boolean = false,
    val isHCVisible: Boolean = false,
    val isWFHClassificationEnabled: Boolean = false,

    val isSchoolDataSamplerEnabled: Boolean = false,
    val isBMIVisible: Boolean = false,
    val isGradeVisible: Boolean = false,

    val showSchoolHeightInFPS: Boolean = false,

    val showTrailingZero: Boolean = false,
)

GLOBAL_DATA_PRECISION and SHOW_TRAILING_ZERO - these two are global variables

ChatGPT's refactored code:

@HiltViewModel
class ApplicationEntryViewModel @Inject constructor(
    private val protoData: DataStore<GeneralApplicationPreferences>
) : ViewModel() {

    private val _applicationStates = MutableStateFlow(GeneralPreferences())
    val applicationStates
        get() = _applicationStates.asStateFlow()

    init {
        viewModelScope.launch {
            protoData.data.collectLatest { preferences ->
                handleDataStoreUpdate(preferences)
            }
        }
    }

    private fun handleDataStoreUpdate(preferences: GeneralApplicationPreferences) {
        GLOBAL_DATA_PRECISION = protoDataPrecisionToDataPrecision(preferences.dataPrecision)

        _applicationStates.value = preferences.toGeneralPreferences()

        SHOW_TRAILING_ZERO = _applicationStates.value.showTrailingZero
    }

    private suspend fun updatePreference(
        updater: suspend (GeneralPreferences) -> GeneralPreferences,
        updaterProto: suspend (GeneralApplicationPreferences) -> GeneralApplicationPreferences
    ) {
        _applicationStates.value = updater(_applicationStates.value)

        viewModelScope.launch {
            protoData.updateData { preferences ->
                updaterProto(preferences)
            }
        }
    }

    private fun GeneralApplicationPreferences.toGeneralPreferences(): GeneralPreferences {
        return GeneralPreferences(
            currentDestination = currentDestination.ifEmpty { NavigationDestinations.SAMCompanion.screen_route },
            isICDSDataSamplerEnabled = isIcdsDataSamplerEnabled,
            isHCVisible = isHcVisible,
            isWFHClassificationEnabled = isWfhClassificationVisible,
            isSchoolDataSamplerEnabled = isSchoolDataSamplerEnabled,
            isBMIVisible = isBmiVisible,
            isGradeVisible = isGradeVisible,
            showSchoolHeightInFPS = showSchoolHeightInFps,
            showTrailingZero = showTrailingZero
        )
    }

    suspend fun handleEventsFor(event: PreferenceEvents) {
        when (event) {
            is PreferenceEvents.DestinationChanged -> updatePreference(
                { it.copy (currentDestination = event.destination) },
                { it.copy { currentDestination = event.destination } }
            )
            is PreferenceEvents.ToggleBMI -> updatePreference(
                { it.copy (isBMIVisible = event.isBMIVisible) },
                { it.copy { isBmiVisible = event.isBMIVisible} }
            )
            is PreferenceEvents.ToggleGrade -> updatePreference(
                { it.copy (isGradeVisible = event.isGradeVisible) },
                { it.copy { isGradeVisible = event.isGradeVisible} }
            )
            is PreferenceEvents.ToggleHC -> updatePreference(
                { it.copy (isHCVisible = event.isHCVisible) },
                { it.copy { isHcVisible = event.isHCVisible} }
            )
            is PreferenceEvents.ToggleWFHClassification -> updatePreference(
                { it.copy (isWFHClassificationEnabled = event.isWFHClassificationEnabled) },
                { it.copy { isWfhClassificationVisible = event.isWFHClassificationEnabled} }
            )
            is PreferenceEvents.ToggleShowHeightInFPS -> updatePreference(
                { it.copy (showSchoolHeightInFPS = event.showHeightInFPS) },
                { it.copy { showSchoolHeightInFps = event.showHeightInFPS} }
            )
            is PreferenceEvents.ToggleTrailingZero -> updatePreference(
                { it.copy (showTrailingZero = event.showTrailingZero) },
                { it.copy { showTrailingZero = event.showTrailingZero} }
            )
            is PreferenceEvents.ToggleGlobalDataPrecision -> updatePreference(
                { it } ,
                { it.copy { dataPrecision = event.precision.toProtoDataPrecision()} }
            )
            is PreferenceEvents.ToggleICDSDataSampler -> updatePreference(
                { it.copy (isICDSDataSamplerEnabled = event.isDataSamplerEnabled) },
                { it.copy { isIcdsDataSamplerEnabled = event.isDataSamplerEnabled} }
            )
            is PreferenceEvents.ToggleSchoolDataSampler -> updatePreference(
                { it.copy (isSchoolDataSamplerEnabled = event.isDataSamplerEnabled) },
                { it.copy { isSchoolDataSamplerEnabled = event.isDataSamplerEnabled} }
            )
        }
    }
}

data class GeneralPreferences (
    val currentDestination: String = NavigationDestinations.School.screen_route,

    val isICDSDataSamplerEnabled: Boolean = false,
    val isHCVisible: Boolean = false,
    val isWFHClassificationEnabled: Boolean = false,

    val isSchoolDataSamplerEnabled: Boolean = false,
    val isBMIVisible: Boolean = false,
    val isGradeVisible: Boolean = false,

    val showSchoolHeightInFPS: Boolean = false,

    val showTrailingZero: Boolean = false,
)

Both the versions are working as intended.

What I observed in the following line (of ChaGPT's code):

is PreferenceEvents.ToggleGlobalDataPrecision -> updatePreference( { it } , { it.copy { dataPrecision = event.precision.toProtoDataPrecision()} } )

At this point I am unable to understand how come the GLOBAL_DATA_PRECISION is getting updated in the ChatGPT's code. Which led me to believe that if ProtoDataStore is updated the rest of the state variables are also automatically updated. Am I right? Or I got the whole thing wrong?

0

There are 0 best solutions below