diff --git a/app/src/main/java/gq/kirmanak/mealient/ui/SwipeRefreshLayoutHelper.kt b/app/src/main/java/gq/kirmanak/mealient/ui/SwipeRefreshLayoutHelper.kt deleted file mode 100644 index 8e76496..0000000 --- a/app/src/main/java/gq/kirmanak/mealient/ui/SwipeRefreshLayoutHelper.kt +++ /dev/null @@ -1,41 +0,0 @@ -package gq.kirmanak.mealient.ui - -import androidx.paging.PagingDataAdapter -import androidx.recyclerview.widget.RecyclerView -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.channels.awaitClose -import kotlinx.coroutines.channels.onFailure -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.callbackFlow -import kotlinx.coroutines.flow.collectLatest -import timber.log.Timber - -@ExperimentalCoroutinesApi -object SwipeRefreshLayoutHelper { - private fun SwipeRefreshLayout.refreshesFlow(): Flow { - Timber.v("refreshesFlow() called") - return callbackFlow { - val listener = SwipeRefreshLayout.OnRefreshListener { - Timber.v("Refresh requested") - trySend(Unit).onFailure { Timber.e(it, "Can't send refresh callback") } - } - Timber.v("Adding refresh request listener") - setOnRefreshListener(listener) - awaitClose { - Timber.v("Removing refresh request listener") - setOnRefreshListener(null) - } - } - } - - suspend fun PagingDataAdapter.listenToRefreshRequests( - refreshLayout: SwipeRefreshLayout - ) { - Timber.v("listenToRefreshRequests() called") - refreshLayout.refreshesFlow().collectLatest { - Timber.d("Received refresh request") - refresh() - } - } -} \ No newline at end of file diff --git a/app/src/main/java/gq/kirmanak/mealient/ui/ViewExtensions.kt b/app/src/main/java/gq/kirmanak/mealient/ui/ViewExtensions.kt new file mode 100644 index 0000000..d424f1c --- /dev/null +++ b/app/src/main/java/gq/kirmanak/mealient/ui/ViewExtensions.kt @@ -0,0 +1,31 @@ +package gq.kirmanak.mealient.ui + +import androidx.lifecycle.LiveData +import androidx.lifecycle.asLiveData +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.channels.onClosed +import kotlinx.coroutines.channels.onFailure +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow +import timber.log.Timber + +@ExperimentalCoroutinesApi +fun SwipeRefreshLayout.refreshesLiveData(): LiveData { + val callbackFlow: Flow = callbackFlow { + val listener = SwipeRefreshLayout.OnRefreshListener { + Timber.v("Refresh requested") + trySend(Unit) + .onFailure { Timber.e(it, "refreshesFlow: can't send refresh callback") } + .onClosed { Timber.e(it, "refreshesFlow: flow has been closed") } + } + Timber.v("Adding refresh request listener") + setOnRefreshListener(listener) + awaitClose { + Timber.v("Removing refresh request listener") + setOnRefreshListener(null) + } + } + return callbackFlow.asLiveData() +} \ No newline at end of file diff --git a/app/src/main/java/gq/kirmanak/mealient/ui/recipes/RecipeViewModel.kt b/app/src/main/java/gq/kirmanak/mealient/ui/recipes/RecipeViewModel.kt index eee108e..f24beec 100644 --- a/app/src/main/java/gq/kirmanak/mealient/ui/recipes/RecipeViewModel.kt +++ b/app/src/main/java/gq/kirmanak/mealient/ui/recipes/RecipeViewModel.kt @@ -1,22 +1,43 @@ package gq.kirmanak.mealient.ui.recipes import android.widget.ImageView +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import androidx.paging.cachedIn import dagger.hilt.android.lifecycle.HiltViewModel import gq.kirmanak.mealient.data.recipes.RecipeImageLoader import gq.kirmanak.mealient.data.recipes.RecipeRepo import gq.kirmanak.mealient.data.recipes.db.entity.RecipeSummaryEntity +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject @HiltViewModel class RecipeViewModel @Inject constructor( - recipeRepo: RecipeRepo, + private val recipeRepo: RecipeRepo, private val recipeImageLoader: RecipeImageLoader ) : ViewModel() { - val recipeFlow = recipeRepo.createPager().flow + private var _isRefreshing = MutableLiveData() + val isRefreshing: LiveData get() = _isRefreshing + + private val _nextRecipeInfoChannel = Channel() + val nextRecipeInfo: Flow = + _nextRecipeInfoChannel.receiveAsFlow() + + val adapter = RecipesPagingAdapter(this) { + Timber.d("onClick: recipe clicked $it") + viewModelScope.launch { _nextRecipeInfoChannel.send(it) } + } + + init { + setupAdapter() + } fun loadRecipeImage(view: ImageView, recipeSummary: RecipeSummaryEntity?) { Timber.v("loadRecipeImage() called with: view = $view, recipeSummary = $recipeSummary") @@ -24,4 +45,21 @@ class RecipeViewModel @Inject constructor( recipeImageLoader.loadRecipeImage(view, recipeSummary?.slug) } } + + private fun setupAdapter() { + with(viewModelScope) { + launch { + recipeRepo.createPager().flow.cachedIn(this).collect { + Timber.d("setupAdapter: received data update") + adapter.submitData(it) + } + } + launch { + adapter.onPagesUpdatedFlow.collect { + Timber.d("setupAdapter: pages have been updated") + _isRefreshing.value = false + } + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/gq/kirmanak/mealient/ui/recipes/RecipesFragment.kt b/app/src/main/java/gq/kirmanak/mealient/ui/recipes/RecipesFragment.kt index d686e92..6b9b638 100644 --- a/app/src/main/java/gq/kirmanak/mealient/ui/recipes/RecipesFragment.kt +++ b/app/src/main/java/gq/kirmanak/mealient/ui/recipes/RecipesFragment.kt @@ -10,12 +10,11 @@ import androidx.fragment.app.viewModels import androidx.lifecycle.Observer import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController -import androidx.recyclerview.widget.LinearLayoutManager import dagger.hilt.android.AndroidEntryPoint import gq.kirmanak.mealient.data.recipes.db.entity.RecipeSummaryEntity import gq.kirmanak.mealient.databinding.FragmentRecipesBinding -import gq.kirmanak.mealient.ui.SwipeRefreshLayoutHelper.listenToRefreshRequests import gq.kirmanak.mealient.ui.auth.AuthenticationViewModel +import gq.kirmanak.mealient.ui.refreshesLiveData import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.collect import timber.log.Timber @@ -74,22 +73,19 @@ class RecipesFragment : Fragment() { private fun setupRecipeAdapter() { Timber.v("setupRecipeAdapter() called") - binding.recipes.layoutManager = LinearLayoutManager(requireContext()) - val adapter = RecipesPagingAdapter(viewModel) { navigateToRecipeInfo(it) } - binding.recipes.adapter = adapter - viewLifecycleOwner.lifecycleScope.launchWhenResumed { - viewModel.recipeFlow.collect { - Timber.d("Received update") - adapter.submitData(it) - } + binding.recipes.adapter = viewModel.adapter + viewModel.isRefreshing.observe(viewLifecycleOwner) { + Timber.d("setupRecipeAdapter: isRefreshing = $it") + binding.refresher.isRefreshing = it + } + binding.refresher.refreshesLiveData().observe(viewLifecycleOwner) { + Timber.d("setupRecipeAdapter: received refresh request") + viewModel.adapter.refresh() } viewLifecycleOwner.lifecycleScope.launchWhenResumed { - adapter.listenToRefreshRequests(binding.refresher) - } - viewLifecycleOwner.lifecycleScope.launchWhenResumed { - adapter.onPagesUpdatedFlow.collect { - Timber.d("Pages have been updated") - binding.refresher.isRefreshing = false + viewModel.nextRecipeInfo.collect { + Timber.d("setupRecipeAdapter: navigating to recipe $it") + navigateToRecipeInfo(it) } } } diff --git a/app/src/main/res/layout/fragment_recipes.xml b/app/src/main/res/layout/fragment_recipes.xml index a39d623..324276e 100644 --- a/app/src/main/res/layout/fragment_recipes.xml +++ b/app/src/main/res/layout/fragment_recipes.xml @@ -19,6 +19,7 @@ android:id="@+id/recipes" android:layout_width="match_parent" android:layout_height="match_parent" + app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" tools:listitem="@layout/view_holder_recipe" />