From 17dbe945e5b21481a0e891aaa1723112e4c506d4 Mon Sep 17 00:00:00 2001 From: Jannik Date: Sun, 15 Oct 2023 18:44:22 +0200 Subject: [PATCH] mograte MyListFragment to use a simple ViewModel fixes crashes in MyListFragment if the User closes the fragment with a loading job still running, part of #56 --- .../main/fragments/MyListsFragment.kt | 39 ++++++++++++--- .../activity/main/viewmodel/HomeViewModel.kt | 2 +- .../viewmodel/MyListsFragmentViewModel.kt | 50 +++++++++++++++++++ 3 files changed, 82 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/org/mosad/teapod/ui/activity/main/viewmodel/MyListsFragmentViewModel.kt diff --git a/app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/MyListsFragment.kt b/app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/MyListsFragment.kt index 4d52918..87b32b0 100644 --- a/app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/MyListsFragment.kt +++ b/app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/MyListsFragment.kt @@ -1,17 +1,21 @@ package org.mosad.teapod.ui.activity.main.fragments import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels +import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import androidx.viewpager2.adapter.FragmentStateAdapter import com.google.android.material.tabs.TabLayoutMediator import kotlinx.coroutines.launch import org.mosad.teapod.R import org.mosad.teapod.databinding.FragmentMyListsBinding -import org.mosad.teapod.parser.crunchyroll.Crunchyroll +import org.mosad.teapod.ui.activity.main.viewmodel.MyListsFragmentViewModel import org.mosad.teapod.util.toItemMediaList class MyListsFragment : Fragment() { @@ -19,6 +23,8 @@ class MyListsFragment : Fragment() { private lateinit var binding: FragmentMyListsBinding private lateinit var pagerAdapter: FragmentStateAdapter + private val model: MyListsFragmentViewModel by viewModels() + private val fragments = arrayListOf() override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { @@ -33,7 +39,6 @@ class MyListsFragment : Fragment() { pagerAdapter = ScreenSlidePagerAdapter(this) binding.pagerMyLists.adapter = pagerAdapter - // TODO is position 0 always episodes? (and 1 always similar titles) TabLayoutMediator(binding.tabMyLists, binding.pagerMyLists) { tab, position -> tab.text = when(position) { 0 -> getString(R.string.my_list) @@ -43,15 +48,33 @@ class MyListsFragment : Fragment() { } }.attach() - lifecycleScope.launch { - val items = Crunchyroll.watchlist(50) - - MediaFragmentSimilar(items.toItemMediaList()).also { - fragments.add(it) - pagerAdapter.notifyItemInserted(fragments.indexOf(it)) + viewLifecycleOwner.lifecycleScope.launch { + viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { + model.onUiState(viewLifecycleOwner.lifecycleScope) { uiState -> + when (uiState) { + is MyListsFragmentViewModel.UiState.Normal -> bindUiStateNormal(uiState) + is MyListsFragmentViewModel.UiState.Loading -> bindUiStateLoading() + is MyListsFragmentViewModel.UiState.Error -> bindUiStateError(uiState) + } + } } } + } + private fun bindUiStateNormal(uiState: MyListsFragmentViewModel.UiState.Normal) { + MediaFragmentSimilar(uiState.watchlistItems.toItemMediaList()).also { + fragments.add(it) + pagerAdapter.notifyItemInserted(fragments.indexOf(it)) + } + } + + private fun bindUiStateLoading() { + // currently not used + } + + private fun bindUiStateError(uiState: MyListsFragmentViewModel.UiState.Error) { + // currently not used + Log.e(javaClass.name, "A error occurred while loading a UiState: ${uiState.message}") } /** diff --git a/app/src/main/java/org/mosad/teapod/ui/activity/main/viewmodel/HomeViewModel.kt b/app/src/main/java/org/mosad/teapod/ui/activity/main/viewmodel/HomeViewModel.kt index 96c9b68..13a0f1f 100644 --- a/app/src/main/java/org/mosad/teapod/ui/activity/main/viewmodel/HomeViewModel.kt +++ b/app/src/main/java/org/mosad/teapod/ui/activity/main/viewmodel/HomeViewModel.kt @@ -32,7 +32,7 @@ import kotlinx.coroutines.launch import org.mosad.teapod.parser.crunchyroll.* import kotlin.random.Random -class HomeViewModel : ViewModel() { +class HomeViewModel : ViewModel() { private val WATCHLIST_LENGTH = 50 diff --git a/app/src/main/java/org/mosad/teapod/ui/activity/main/viewmodel/MyListsFragmentViewModel.kt b/app/src/main/java/org/mosad/teapod/ui/activity/main/viewmodel/MyListsFragmentViewModel.kt new file mode 100644 index 0000000..348ab7a --- /dev/null +++ b/app/src/main/java/org/mosad/teapod/ui/activity/main/viewmodel/MyListsFragmentViewModel.kt @@ -0,0 +1,50 @@ +package org.mosad.teapod.ui.activity.main.viewmodel + +import androidx.lifecycle.LifecycleCoroutineScope +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.async +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.launch +import org.mosad.teapod.parser.crunchyroll.Crunchyroll +import org.mosad.teapod.parser.crunchyroll.Item + +class MyListsFragmentViewModel : ViewModel() { + + private val WATCHLIST_LENGTH = 50 + + private val uiState = MutableStateFlow(UiState.Loading) + + sealed class UiState { + object Loading : UiState() + data class Normal( + val watchlistItems: List + ) : UiState() + data class Error(val message: String?) : UiState() + } + + init { + load() + } + + fun onUiState(scope: LifecycleCoroutineScope, collector: (UiState) -> Unit) { + scope.launch { uiState.collect { collector(it) } } + } + + fun load() { + viewModelScope.launch { + uiState.emit(UiState.Loading) + try { + // run the loading in parallel to speed up the process + val watchlistJob = viewModelScope.async { Crunchyroll.watchlist(WATCHLIST_LENGTH).data } + uiState.emit( + UiState.Normal(watchlistJob.await()) + ) + } catch (e: Exception) { + uiState.emit(UiState.Error(e.message)) + } + } + + } + +} \ No newline at end of file