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
This commit is contained in:
parent
5f609d4c33
commit
17dbe945e5
|
@ -1,17 +1,21 @@
|
||||||
package org.mosad.teapod.ui.activity.main.fragments
|
package org.mosad.teapod.ui.activity.main.fragments
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.fragment.app.viewModels
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||||
import com.google.android.material.tabs.TabLayoutMediator
|
import com.google.android.material.tabs.TabLayoutMediator
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.mosad.teapod.R
|
import org.mosad.teapod.R
|
||||||
import org.mosad.teapod.databinding.FragmentMyListsBinding
|
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
|
import org.mosad.teapod.util.toItemMediaList
|
||||||
|
|
||||||
class MyListsFragment : Fragment() {
|
class MyListsFragment : Fragment() {
|
||||||
|
@ -19,6 +23,8 @@ class MyListsFragment : Fragment() {
|
||||||
private lateinit var binding: FragmentMyListsBinding
|
private lateinit var binding: FragmentMyListsBinding
|
||||||
private lateinit var pagerAdapter: FragmentStateAdapter
|
private lateinit var pagerAdapter: FragmentStateAdapter
|
||||||
|
|
||||||
|
private val model: MyListsFragmentViewModel by viewModels()
|
||||||
|
|
||||||
private val fragments = arrayListOf<Fragment>()
|
private val fragments = arrayListOf<Fragment>()
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||||
|
@ -33,7 +39,6 @@ class MyListsFragment : Fragment() {
|
||||||
pagerAdapter = ScreenSlidePagerAdapter(this)
|
pagerAdapter = ScreenSlidePagerAdapter(this)
|
||||||
binding.pagerMyLists.adapter = pagerAdapter
|
binding.pagerMyLists.adapter = pagerAdapter
|
||||||
|
|
||||||
// TODO is position 0 always episodes? (and 1 always similar titles)
|
|
||||||
TabLayoutMediator(binding.tabMyLists, binding.pagerMyLists) { tab, position ->
|
TabLayoutMediator(binding.tabMyLists, binding.pagerMyLists) { tab, position ->
|
||||||
tab.text = when(position) {
|
tab.text = when(position) {
|
||||||
0 -> getString(R.string.my_list)
|
0 -> getString(R.string.my_list)
|
||||||
|
@ -43,15 +48,33 @@ class MyListsFragment : Fragment() {
|
||||||
}
|
}
|
||||||
}.attach()
|
}.attach()
|
||||||
|
|
||||||
lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
val items = Crunchyroll.watchlist(50)
|
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||||
|
model.onUiState(viewLifecycleOwner.lifecycleScope) { uiState ->
|
||||||
MediaFragmentSimilar(items.toItemMediaList()).also {
|
when (uiState) {
|
||||||
fragments.add(it)
|
is MyListsFragmentViewModel.UiState.Normal -> bindUiStateNormal(uiState)
|
||||||
pagerAdapter.notifyItemInserted(fragments.indexOf(it))
|
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}")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -32,7 +32,7 @@ import kotlinx.coroutines.launch
|
||||||
import org.mosad.teapod.parser.crunchyroll.*
|
import org.mosad.teapod.parser.crunchyroll.*
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
class HomeViewModel : ViewModel() {
|
class HomeViewModel : ViewModel() {
|
||||||
|
|
||||||
private val WATCHLIST_LENGTH = 50
|
private val WATCHLIST_LENGTH = 50
|
||||||
|
|
||||||
|
|
|
@ -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>(UiState.Loading)
|
||||||
|
|
||||||
|
sealed class UiState {
|
||||||
|
object Loading : UiState()
|
||||||
|
data class Normal(
|
||||||
|
val watchlistItems: List<Item>
|
||||||
|
) : 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue