From 746c977e99afa5f2537655798673a0a95921dab6 Mon Sep 17 00:00:00 2001 From: "Don E. Llopis" Date: Sat, 21 Aug 2021 07:49:50 -0400 Subject: [PATCH] refactor ui state handling - simplified view logic update target jdk to 11 bump library versions to latest available --- app/build.gradle | 22 +++---- .../apodktm/ApodViewModel.kt | 41 ++++++------ .../apodktm/ApodsListFragment.kt | 65 +++++++------------ build.gradle | 3 +- 4 files changed, 57 insertions(+), 74 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index fd86199..1996568 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -32,11 +32,11 @@ android { } } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 } kotlinOptions { - jvmTarget = '1.8' + jvmTarget = '11' } buildFeatures { viewBinding true @@ -46,16 +46,16 @@ android { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation 'androidx.core:core-ktx:1.5.0' - implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'com.google.android.material:material:1.3.0' - implementation 'androidx.constraintlayout:constraintlayout:2.0.4' + implementation 'androidx.core:core-ktx:1.6.0' + implementation 'androidx.appcompat:appcompat:1.3.1' + implementation 'com.google.android.material:material:1.4.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0' testImplementation 'junit:junit:4.+' - androidTestImplementation 'androidx.test.ext:junit:1.1.2' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' - def lifecycle_version = "2.4.0-alpha02" + def lifecycle_version = "2.4.0-alpha03" implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version" @@ -82,7 +82,7 @@ dependencies { implementation "org.jetbrains.kotlinx:kotlinx-datetime:0.2.1" - implementation "io.coil-kt:coil:1.2.2" + implementation "io.coil-kt:coil:1.3.1" implementation 'com.github.Baseflow:PhotoView:2.3.0' } \ No newline at end of file diff --git a/app/src/main/java/com/machineinteractive/apodktm/ApodViewModel.kt b/app/src/main/java/com/machineinteractive/apodktm/ApodViewModel.kt index e22c473..6eeec27 100644 --- a/app/src/main/java/com/machineinteractive/apodktm/ApodViewModel.kt +++ b/app/src/main/java/com/machineinteractive/apodktm/ApodViewModel.kt @@ -16,15 +16,12 @@ class ApodViewModel @Inject constructor(private val repository: ApodRepository) private val curDate = MutableStateFlow(maxDate) private var pickerCurDate = maxDate - val apods: Flow> = curDate.flatMapLatest { + @ExperimentalCoroutinesApi + private val apods: Flow> = curDate.flatMapLatest { repository.getApodsForCurMonth(it) - }.stateIn( - viewModelScope, - SharingStarted.Eagerly, - emptyList() - ) + } - private val _apodListUiState = MutableStateFlow(ApodListUiState.Idle) + private val _apodListUiState = MutableStateFlow(ApodListUiState()) val apodListUiState: StateFlow = _apodListUiState private val _selectedApod: MutableStateFlow = MutableStateFlow(null) @@ -38,6 +35,12 @@ class ApodViewModel @Inject constructor(private val repository: ApodRepository) init { Log.d(TAG, "ApodViewModel.init...") + viewModelScope.launch { + apods.collect { + _apodListUiState.value = _apodListUiState.value.copy(apods = it) + } + } + fetchApods() } fun select(apod: Apod) { @@ -49,25 +52,22 @@ class ApodViewModel @Inject constructor(private val repository: ApodRepository) fun fetchApods() { Log.d(TAG, "ApodViewModel.fetchApods - curDate: ${curDate.value} ...") curJob = viewModelScope.launch(Dispatchers.IO) { - if (!isActive) return@launch - var hasApods = repository.curMonthHasApods(curDate.value) - Log.d(TAG, "hasApods: $hasApods") if (!isActive) return@launch if (repository.needsUpdate(curDate.value)) { if (!isActive) return@launch + var hasApods = repository.curMonthHasApods(curDate.value) + Log.d(TAG, "hasApods: $hasApods") if (!hasApods) { - _apodListUiState.value = ApodListUiState.Loading + _apodListUiState.value = _apodListUiState.value.copy(loading = true) } when (val result = repository.updateApodsForCurMonth(curDate.value)) { is ApodResult.Error -> { - hasApods = repository.curMonthHasApods(curDate.value) - _apodListUiState.value = ApodListUiState.Error(hasApods, result) + _apodListUiState.value = _apodListUiState.value.copy(error = result) return@launch } } } - hasApods = repository.curMonthHasApods(curDate.value) - _apodListUiState.value = ApodListUiState.Success(hasApods) + _apodListUiState.value = _apodListUiState.value.copy(loading = false) } } @@ -189,9 +189,8 @@ data class BottomNavBarUiState( data class PickerUiState(val today: LocalDate, val curPickerDate: LocalDate) -sealed class ApodListUiState { - object Idle : ApodListUiState() - object Loading : ApodListUiState() - class Error(val hasApods: Boolean, val error: ApodResult.Error) : ApodListUiState() - class Success(val hasApods: Boolean) : ApodListUiState() -} \ No newline at end of file +data class ApodListUiState( + val loading: Boolean = false, + val error: ApodResult.Error? = null, + val apods: List = emptyList() +) diff --git a/app/src/main/java/com/machineinteractive/apodktm/ApodsListFragment.kt b/app/src/main/java/com/machineinteractive/apodktm/ApodsListFragment.kt index c76ffe5..97211d5 100644 --- a/app/src/main/java/com/machineinteractive/apodktm/ApodsListFragment.kt +++ b/app/src/main/java/com/machineinteractive/apodktm/ApodsListFragment.kt @@ -76,12 +76,6 @@ class ApodsListFragment : Fragment(), ApodAdapter.Listener { updateUi(state) } } - launch { - viewModel.apods.collect { - adapter.submitList(it) - } - } - viewModel.fetchApods() } } } @@ -96,45 +90,36 @@ class ApodsListFragment : Fragment(), ApodAdapter.Listener { } private fun updateUi(state: ApodListUiState) { - when (state) { - is ApodListUiState.Idle -> { - // NO-OP - } - is ApodListUiState.Loading -> { - Log.d(TAG, "loading...") - showProgressBar(true) - adapter.submitList(emptyList()) - binding.emptyText.visibility = View.INVISIBLE - } - is ApodListUiState.Error -> { - Log.d(TAG, "error...") - showProgressBar(false) - toggleEmptyState(state.hasApods) - snackbar = Snackbar.make( - binding.snackbarArea, - "${state.error.message}", - Snackbar.LENGTH_INDEFINITE - ) - snackbar?.setAction(getString(R.string.retry)) { - snackbar?.dismiss() - snackbar = null - viewModel.fetchApods() - } - snackbar?.show() - } - is ApodListUiState.Success -> { - Log.d(TAG, "success...") - showProgressBar(false) - toggleEmptyState(state.hasApods) + showProgressBar(state.loading) + + if (state.error != null) { + snackbar = Snackbar.make( + binding.snackbarArea, + "${state.error.message}", + Snackbar.LENGTH_INDEFINITE + ) + snackbar?.setAction(getString(R.string.retry)) { + snackbar?.dismiss() + snackbar = null + viewModel.fetchApods() } + snackbar?.show() } - } - private fun toggleEmptyState(hasApods: Boolean) { - binding.emptyText.visibility = if (hasApods) { - View.INVISIBLE + adapter.submitList(state.apods) + + if (state.apods.isEmpty() && !state.loading) { + toggleEmptyState(true) } else { + toggleEmptyState(false) + } + } + + private fun toggleEmptyState(value: Boolean) { + binding.emptyText.visibility = if (value) { View.VISIBLE + } else { + View.INVISIBLE } } diff --git a/build.gradle b/build.gradle index e4dbbf2..8daa224 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { ext { - kotlin_version = "1.5.10" + kotlin_version = "1.5.21" nav_version = "2.3.5" hilt_version = "2.37" } @@ -26,7 +26,6 @@ allprojects { repositories { google() mavenCentral() - jcenter() // Warning: this repository is going to shut down soon maven { url 'https://jitpack.io' } } }