Fix jumping recipe info sheet
This commit is contained in:
@@ -10,4 +10,6 @@ interface RecipeRepo {
|
||||
suspend fun clearLocalData()
|
||||
|
||||
suspend fun loadRecipeInfo(recipeId: String, recipeSlug: String): FullRecipeEntity
|
||||
|
||||
suspend fun loadRecipeInfoFromDb(recipeId: String, recipeSlug: String): FullRecipeEntity?
|
||||
}
|
||||
@@ -49,4 +49,14 @@ class RecipeRepoImpl @Inject constructor(
|
||||
|
||||
return storage.queryRecipeInfo(recipeId)
|
||||
}
|
||||
|
||||
override suspend fun loadRecipeInfoFromDb(
|
||||
recipeId: String,
|
||||
recipeSlug: String
|
||||
): FullRecipeEntity? {
|
||||
logger.v { "loadRecipeInfoFromDb() called with: recipeId = $recipeId, recipeSlug = $recipeSlug" }
|
||||
return runCatchingExceptCancel { storage.queryRecipeInfo(recipeId) }
|
||||
.onFailure { logger.e(it) { "loadRecipeInfoFromDb failed" } }
|
||||
.getOrNull()
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
package gq.kirmanak.mealient.ui.recipes
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.*
|
||||
import androidx.paging.cachedIn
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import gq.kirmanak.mealient.data.auth.AuthRepo
|
||||
import gq.kirmanak.mealient.data.recipes.RecipeRepo
|
||||
import gq.kirmanak.mealient.database.recipe.entity.FullRecipeEntity
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity
|
||||
import gq.kirmanak.mealient.datasource.runCatchingExceptCancel
|
||||
import gq.kirmanak.mealient.extensions.valueUpdatesOnly
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
@@ -16,7 +16,7 @@ import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class RecipeViewModel @Inject constructor(
|
||||
recipeRepo: RecipeRepo,
|
||||
private val recipeRepo: RecipeRepo,
|
||||
authRepo: AuthRepo,
|
||||
private val logger: Logger,
|
||||
) : ViewModel() {
|
||||
@@ -37,4 +37,13 @@ class RecipeViewModel @Inject constructor(
|
||||
logger.v { "onAuthorizationSuccessHandled() called" }
|
||||
_isAuthorized.postValue(null)
|
||||
}
|
||||
|
||||
fun loadRecipeInfo(
|
||||
summaryEntity: RecipeSummaryEntity
|
||||
): LiveData<Result<FullRecipeEntity>> = liveData {
|
||||
val result = runCatchingExceptCancel {
|
||||
recipeRepo.loadRecipeInfo(summaryEntity.remoteId, summaryEntity.slug)
|
||||
}
|
||||
emit(result)
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package gq.kirmanak.mealient.ui.recipes
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.fragment.app.viewModels
|
||||
@@ -16,10 +17,7 @@ import gq.kirmanak.mealient.R
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity
|
||||
import gq.kirmanak.mealient.databinding.FragmentRecipesBinding
|
||||
import gq.kirmanak.mealient.datasource.NetworkError
|
||||
import gq.kirmanak.mealient.extensions.collectWhenViewResumed
|
||||
import gq.kirmanak.mealient.extensions.refreshRequestFlow
|
||||
import gq.kirmanak.mealient.extensions.showLongToast
|
||||
import gq.kirmanak.mealient.extensions.valueUpdatesOnly
|
||||
import gq.kirmanak.mealient.extensions.*
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import gq.kirmanak.mealient.ui.activity.MainActivityViewModel
|
||||
import gq.kirmanak.mealient.ui.recipes.images.RecipePreloaderFactory
|
||||
@@ -45,6 +43,8 @@ class RecipesFragment : Fragment(R.layout.fragment_recipes) {
|
||||
@Inject
|
||||
lateinit var recipePreloaderFactory: RecipePreloaderFactory
|
||||
|
||||
private var ignoreRecipeClicks = false
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
logger.v { "onViewCreated() called with: view = $view, savedInstanceState = $savedInstanceState" }
|
||||
@@ -63,10 +63,22 @@ class RecipesFragment : Fragment(R.layout.fragment_recipes) {
|
||||
)
|
||||
}
|
||||
|
||||
private fun onRecipeClicked(recipe: RecipeSummaryEntity) {
|
||||
logger.d { "onRecipeClicked() called with: recipe = $recipe" }
|
||||
if (ignoreRecipeClicks) return
|
||||
binding.progress.isVisible = true
|
||||
ignoreRecipeClicks = true // TODO doesn't really work
|
||||
viewModel.loadRecipeInfo(recipe).observeOnce(viewLifecycleOwner) { result ->
|
||||
binding.progress.isVisible = false
|
||||
if (result.isSuccess) navigateToRecipeInfo(recipe)
|
||||
ignoreRecipeClicks = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupRecipeAdapter() {
|
||||
logger.v { "setupRecipeAdapter() called" }
|
||||
|
||||
val recipesAdapter = recipePagingAdapterFactory.build { navigateToRecipeInfo(it) }
|
||||
val recipesAdapter = recipePagingAdapterFactory.build { onRecipeClicked(it) }
|
||||
|
||||
with(binding.recipes) {
|
||||
adapter = recipesAdapter
|
||||
@@ -135,17 +147,11 @@ private fun Throwable.toLoadErrorReasonText(): Int? = when (this) {
|
||||
}
|
||||
|
||||
private fun <T : Any, VH : RecyclerView.ViewHolder> PagingDataAdapter<T, VH>.refreshErrors(): Flow<Throwable> {
|
||||
return loadStateFlow
|
||||
.map { it.refresh }
|
||||
.valueUpdatesOnly()
|
||||
.filterIsInstance<LoadState.Error>()
|
||||
return loadStateFlow.map { it.refresh }.valueUpdatesOnly().filterIsInstance<LoadState.Error>()
|
||||
.map { it.error }
|
||||
}
|
||||
|
||||
private fun <T : Any, VH : RecyclerView.ViewHolder> PagingDataAdapter<T, VH>.appendPaginationEnd(): Flow<Unit> {
|
||||
return loadStateFlow
|
||||
.map { it.append.endOfPaginationReached }
|
||||
.valueUpdatesOnly()
|
||||
.filter { it }
|
||||
return loadStateFlow.map { it.append.endOfPaginationReached }.valueUpdatesOnly().filter { it }
|
||||
.map { }
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.liveData
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import gq.kirmanak.mealient.data.recipes.RecipeRepo
|
||||
import gq.kirmanak.mealient.datasource.runCatchingExceptCancel
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -21,20 +20,14 @@ class RecipeInfoViewModel @Inject constructor(
|
||||
|
||||
val uiState: LiveData<RecipeInfoUiState> = liveData {
|
||||
logger.v { "Initializing UI state with args = $args" }
|
||||
emit(RecipeInfoUiState())
|
||||
runCatchingExceptCancel {
|
||||
recipeRepo.loadRecipeInfo(args.recipeId, args.recipeSlug)
|
||||
}.onSuccess {
|
||||
logger.d { "loadRecipeInfo: received recipe info = $it" }
|
||||
val newState = RecipeInfoUiState(
|
||||
val state = recipeRepo.loadRecipeInfoFromDb(args.recipeId, args.recipeSlug)?.let {
|
||||
RecipeInfoUiState(
|
||||
areIngredientsVisible = it.recipeIngredients.isNotEmpty(),
|
||||
areInstructionsVisible = it.recipeInstructions.isNotEmpty(),
|
||||
recipeInfo = it,
|
||||
)
|
||||
emit(newState)
|
||||
}.onFailure {
|
||||
logger.e(it) { "loadRecipeInfo: can't load recipe info" }
|
||||
}
|
||||
} ?: RecipeInfoUiState()
|
||||
emit(state)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,4 +19,10 @@
|
||||
tools:listitem="@layout/view_holder_recipe" />
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
android:id="@+id/progress"
|
||||
style="@style/IndeterminateProgress"
|
||||
android:layout_width="wrap_content"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
Reference in New Issue
Block a user