Provide better error messages with stacktrace (#119)

* Provide better error messages with stacktrace

+ clean up
+ fix error details popup in MediaInfoFragment

* Simplify exception passing by sending complete exception to the dialog

* Use viewLifecycleOwner with repeatOnLifecycle
This commit is contained in:
Jarne Demeulemeester 2022-06-06 14:41:37 +02:00 committed by GitHub
parent 741083da40
commit 751ee75c3e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 74 additions and 99 deletions

View file

@ -8,18 +8,21 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dev.jdtech.jellyfin.R
import java.lang.IllegalStateException
class ErrorDialogFragment(private val errorMessage: String) : DialogFragment() {
class ErrorDialogFragment(
private val error: Exception
) : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return activity?.let {
val builder = MaterialAlertDialogBuilder(it, R.style.ErrorDialogStyle)
builder
.setMessage(errorMessage)
.setTitle(error.message ?: getString(R.string.unknown_error))
.setMessage(error.stackTraceToString())
.setPositiveButton(getString(R.string.close)) { _, _ ->
}
.setNeutralButton(getString(R.string.share)) { _, _ ->
val sendIntent: Intent = Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, errorMessage)
putExtra(Intent.EXTRA_TEXT, "${error.message}\n ${error.stackTraceToString()}")
type = "text/plain"
}

View file

@ -12,7 +12,6 @@ import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import dagger.hilt.android.AndroidEntryPoint
import dev.jdtech.jellyfin.R
import dev.jdtech.jellyfin.adapters.*
import dev.jdtech.jellyfin.databinding.FragmentDownloadBinding
import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
@ -86,12 +85,11 @@ class DownloadFragment : Fragment() {
}
private fun bindUiStateError(uiState: DownloadViewModel.UiState.Error) {
val error = uiState.message ?: resources.getString(R.string.unknown_error)
errorDialog = ErrorDialogFragment(error)
errorDialog = ErrorDialogFragment(uiState.error)
binding.loadingIndicator.isVisible = false
binding.downloadsRecyclerView.isVisible = false
binding.errorLayout.errorPanel.isVisible = true
checkIfLoginRequired(error)
checkIfLoginRequired(uiState.error.message)
}
private fun navigateToMediaInfoFragment(item: PlayerItem) {

View file

@ -207,7 +207,7 @@ class EpisodeBottomSheetFragment : BottomSheetDialogFragment() {
private fun bindUiStateError(uiState: EpisodeBottomSheetViewModel.UiState.Error) {
binding.loadingIndicator.isVisible = false
binding.overview.text = uiState.message
binding.overview.text = uiState.error.message
}
private fun bindPlayerItems(items: PlayerViewModel.PlayerItems) {
@ -222,7 +222,7 @@ class EpisodeBottomSheetFragment : BottomSheetDialogFragment() {
}
private fun bindPlayerItemsError(error: PlayerViewModel.PlayerItemError) {
Timber.e(error.message)
Timber.e(error.error.message)
binding.playerItemsError.isVisible = true
binding.playButton.setImageDrawable(
@ -233,7 +233,7 @@ class EpisodeBottomSheetFragment : BottomSheetDialogFragment() {
)
binding.progressCircular.visibility = View.INVISIBLE
binding.playerItemsErrorDetails.setOnClickListener {
ErrorDialogFragment(error.message).show(parentFragmentManager, "errordialog")
ErrorDialogFragment(error.error).show(parentFragmentManager, "errordialog")
}
}

View file

@ -12,7 +12,6 @@ import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import dagger.hilt.android.AndroidEntryPoint
import dev.jdtech.jellyfin.R
import dev.jdtech.jellyfin.adapters.FavoritesListAdapter
import dev.jdtech.jellyfin.adapters.HomeEpisodeListAdapter
import dev.jdtech.jellyfin.adapters.ViewItemListAdapter
@ -87,12 +86,11 @@ class FavoriteFragment : Fragment() {
}
private fun bindUiStateError(uiState: FavoriteViewModel.UiState.Error) {
val error = uiState.message ?: resources.getString(R.string.unknown_error)
errorDialog = ErrorDialogFragment(error)
errorDialog = ErrorDialogFragment(uiState.error)
binding.loadingIndicator.isVisible = false
binding.favoritesRecyclerView.isVisible = false
binding.errorLayout.errorPanel.isVisible = true
checkIfLoginRequired(error)
checkIfLoginRequired(uiState.error.message)
}
private fun navigateToMediaInfoFragment(item: BaseItemDto) {

View file

@ -139,13 +139,12 @@ class HomeFragment : Fragment() {
}
private fun bindUiStateError(uiState: HomeViewModel.UiState.Error) {
val error = uiState.message ?: getString(R.string.unknown_error)
errorDialog = ErrorDialogFragment(error)
errorDialog = ErrorDialogFragment(uiState.error)
binding.loadingIndicator.isVisible = false
binding.refreshLayout.isRefreshing = false
binding.viewsRecyclerView.isVisible = false
binding.errorLayout.errorPanel.isVisible = true
checkIfLoginRequired(error)
checkIfLoginRequired(uiState.error.message)
}
private fun navigateToLibraryFragment(view: dev.jdtech.jellyfin.models.View) {

View file

@ -132,12 +132,11 @@ class LibraryFragment : Fragment() {
}
private fun bindUiStateError(uiState: LibraryViewModel.UiState.Error) {
val error = uiState.message ?: getString(R.string.unknown_error)
errorDialog = ErrorDialogFragment(error)
errorDialog = ErrorDialogFragment(uiState.error)
binding.loadingIndicator.isVisible = false
binding.itemsRecyclerView.isVisible = false
binding.errorLayout.errorPanel.isVisible = true
checkIfLoginRequired(error)
checkIfLoginRequired(uiState.error.message)
}
private fun navigateToMediaInfoFragment(item: BaseItemDto) {

View file

@ -45,7 +45,7 @@ class LoginFragment : Fragment() {
}
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.onUiState(viewLifecycleOwner.lifecycleScope) { uiState ->
Timber.d("$uiState")
when(uiState) {

View file

@ -120,12 +120,11 @@ class MediaFragment : Fragment() {
}
private fun bindUiStateError(uiState: MediaViewModel.UiState.Error) {
val error = uiState.message ?: resources.getString(R.string.unknown_error)
errorDialog = ErrorDialogFragment(error)
errorDialog = ErrorDialogFragment(uiState.error)
binding.loadingIndicator.isVisible = false
binding.viewsRecyclerView.isVisible = false
binding.errorLayout.errorPanel.isVisible = true
checkIfLoginRequired(error)
checkIfLoginRequired(uiState.error.message)
}

View file

@ -145,6 +145,10 @@ class MediaInfoFragment : Fragment() {
viewModel.loadData(args.itemId, args.itemType)
}
binding.errorLayout.errorDetailsButton.setOnClickListener {
errorDialog.show(parentFragmentManager, "errordialog")
}
binding.checkButton.setOnClickListener {
when (viewModel.played) {
true -> {
@ -286,11 +290,11 @@ class MediaInfoFragment : Fragment() {
}
private fun bindUiStateError(uiState: MediaInfoViewModel.UiState.Error) {
val error = uiState.message ?: getString(R.string.unknown_error)
errorDialog = ErrorDialogFragment(uiState.error)
binding.loadingIndicator.isVisible = false
binding.mediaInfoScrollview.isVisible = false
binding.errorLayout.errorPanel.isVisible = true
checkIfLoginRequired(error)
checkIfLoginRequired(uiState.error.message)
}
private fun bindPlayerItems(items: PlayerViewModel.PlayerItems) {
@ -305,7 +309,7 @@ class MediaInfoFragment : Fragment() {
}
private fun bindPlayerItemsError(error: PlayerViewModel.PlayerItemError) {
Timber.e(error.message)
Timber.e(error.error.message)
binding.playerItemsError.visibility = View.VISIBLE
binding.playButton.setImageDrawable(
ContextCompat.getDrawable(
@ -315,7 +319,7 @@ class MediaInfoFragment : Fragment() {
)
binding.progressCircular.visibility = View.INVISIBLE
binding.playerItemsErrorDetails.setOnClickListener {
ErrorDialogFragment(error.message).show(parentFragmentManager, "errordialog")
ErrorDialogFragment(error.error).show(parentFragmentManager, "errordialog")
}
}

View file

@ -104,12 +104,11 @@ internal class PersonDetailFragment : Fragment() {
}
private fun bindUiStateError(uiState: PersonDetailViewModel.UiState.Error) {
val error = uiState.message ?: resources.getString(R.string.unknown_error)
errorDialog = ErrorDialogFragment(error)
errorDialog = ErrorDialogFragment(uiState.error)
binding.loadingIndicator.isVisible = false
binding.fragmentContent.isVisible = false
binding.errorLayout.errorPanel.isVisible = true
checkIfLoginRequired(error)
checkIfLoginRequired(uiState.error.message)
}
private fun adapter() = ViewItemListAdapter(

View file

@ -13,7 +13,6 @@ import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import dagger.hilt.android.AndroidEntryPoint
import dev.jdtech.jellyfin.R
import dev.jdtech.jellyfin.adapters.FavoritesListAdapter
import dev.jdtech.jellyfin.adapters.HomeEpisodeListAdapter
import dev.jdtech.jellyfin.adapters.ViewItemListAdapter
@ -91,12 +90,11 @@ class SearchResultFragment : Fragment() {
}
private fun bindUiStateError(uiState: SearchResultViewModel.UiState.Error) {
val error = uiState.message ?: getString(R.string.unknown_error)
errorDialog = ErrorDialogFragment(error)
errorDialog = ErrorDialogFragment(uiState.error)
binding.loadingIndicator.isVisible = false
binding.searchResultsRecyclerView.isVisible = false
binding.errorLayout.errorPanel.isVisible = true
checkIfLoginRequired(error)
checkIfLoginRequired(uiState.error.message)
}
private fun navigateToMediaInfoFragment(item: BaseItemDto) {

View file

@ -13,7 +13,6 @@ import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import dagger.hilt.android.AndroidEntryPoint
import dev.jdtech.jellyfin.R
import dev.jdtech.jellyfin.adapters.EpisodeListAdapter
import dev.jdtech.jellyfin.databinding.FragmentSeasonBinding
import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
@ -88,12 +87,11 @@ class SeasonFragment : Fragment() {
}
private fun bindUiStateError(uiState: SeasonViewModel.UiState.Error) {
val error = uiState.message ?: getString(R.string.unknown_error)
errorDialog = ErrorDialogFragment(error)
errorDialog = ErrorDialogFragment(uiState.error)
binding.loadingIndicator.isVisible = false
binding.episodesRecyclerView.isVisible = false
binding.errorLayout.errorPanel.isVisible = true
checkIfLoginRequired(error)
checkIfLoginRequired(uiState.error.message)
}
private fun navigateToEpisodeBottomSheetFragment(episode: BaseItemDto) {

View file

@ -49,7 +49,7 @@ class ServerSelectFragment : Fragment() {
}
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.onNavigateToMain(viewLifecycleOwner.lifecycleScope) {
if (it) {
navigateToMainActivity()

View file

@ -248,7 +248,7 @@ internal class MediaDetailFragment : Fragment() {
}
private fun bindPlayerItemsError(error: PlayerItemError) {
Timber.e(error.message)
Timber.e(error.error.message)
binding.errorLayout.errorPanel.isVisible = true
binding.playButton.setImageDrawable(

View file

@ -40,7 +40,7 @@ class TvLoginFragment : Fragment() {
}
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.onUiState(viewLifecycleOwner.lifecycleScope) { uiState ->
Timber.d("$uiState")
when(uiState) {

View file

@ -32,10 +32,12 @@ fun BaseItemDto.contentType() = when (type) {
else -> ContentType.UNKNOWN
}
fun Fragment.checkIfLoginRequired(error: String) {
if (error.contains("401")) {
Timber.d("Login required!")
findNavController().navigate(AppNavigationDirections.actionGlobalLoginFragment())
fun Fragment.checkIfLoginRequired(error: String?) {
if (error != null) {
if (error.contains("401")) {
Timber.d("Login required!")
findNavController().navigate(AppNavigationDirections.actionGlobalLoginFragment())
}
}
}

View file

@ -95,8 +95,7 @@ constructor(
}
okServers.isNotEmpty() -> {
val okServer = okServers.first()
val issuesString = createIssuesString(okServer)
throw Exception(issuesString)
throw Exception(createIssuesString(okServer))
}
else -> {
throw Exception(resources.getString(R.string.add_server_error_not_found))

View file

@ -8,7 +8,6 @@ import dev.jdtech.jellyfin.models.DownloadSection
import dev.jdtech.jellyfin.utils.loadDownloadedEpisodes
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import java.util.*
import javax.inject.Inject
@ -24,7 +23,7 @@ constructor(
sealed class UiState {
data class Normal(val downloadSections: List<DownloadSection>) : UiState()
object Loading : UiState()
data class Error(val message: String?) : UiState()
data class Error(val error: Exception) : UiState()
}
fun onUiState(scope: LifecycleCoroutineScope, collector: (UiState) -> Unit) {
@ -40,10 +39,6 @@ constructor(
uiState.emit(UiState.Loading)
try {
val items = loadDownloadedEpisodes(downloadDatabase)
if (items.isEmpty()) {
uiState.emit(UiState.Normal(emptyList()))
//return@launch
}
val downloadSections = mutableListOf<DownloadSection>()
withContext(Dispatchers.Default) {
DownloadSection(
@ -65,7 +60,7 @@ constructor(
}
uiState.emit(UiState.Normal(downloadSections))
} catch (e: Exception) {
uiState.emit(UiState.Error(e.message))
uiState.emit(UiState.Error(e))
}
}
}

View file

@ -45,7 +45,7 @@ constructor(
) : UiState()
object Loading : UiState()
data class Error(val message: String?) : UiState()
data class Error(val error: Exception) : UiState()
}
fun onUiState(scope: LifecycleCoroutineScope, collector: (UiState) -> Unit) {
@ -91,7 +91,7 @@ constructor(
)
)
} catch (e: Exception) {
uiState.emit(UiState.Error(e.message))
uiState.emit(UiState.Error(e))
}
}
}
@ -170,7 +170,12 @@ constructor(
val metadata = baseItemDtoToDownloadMetadata(episode)
downloadRequestItem = DownloadRequestItem(uri, itemId, metadata)
downloadEpisode = true
requestDownload(downloadDatabase, Uri.parse(downloadRequestItem.uri), downloadRequestItem, application)
requestDownload(
downloadDatabase,
Uri.parse(downloadRequestItem.uri),
downloadRequestItem,
application
)
}
}

View file

@ -8,7 +8,6 @@ import dev.jdtech.jellyfin.models.FavoriteSection
import dev.jdtech.jellyfin.repository.JellyfinRepository
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.*
@ -25,7 +24,7 @@ constructor(
sealed class UiState {
data class Normal(val favoriteSections: List<FavoriteSection>) : UiState()
object Loading : UiState()
data class Error(val message: String?) : UiState()
data class Error(val error: Exception) : UiState()
}
fun onUiState(scope: LifecycleCoroutineScope, collector: (UiState) -> Unit) {
@ -78,7 +77,7 @@ constructor(
uiState.emit(UiState.Normal(favoriteSections))
} catch (e: Exception) {
uiState.emit(UiState.Error(e.message))
uiState.emit(UiState.Error(e))
}
}
}

View file

@ -17,7 +17,6 @@ import dev.jdtech.jellyfin.utils.syncPlaybackProgress
import dev.jdtech.jellyfin.utils.toView
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.*
@ -34,7 +33,7 @@ class HomeViewModel @Inject internal constructor(
sealed class UiState {
data class Normal(val homeItems: List<HomeItem>) : UiState()
object Loading : UiState()
data class Error(val message: String?) : UiState()
data class Error(val error: Exception) : UiState()
}
fun onUiState(scope: LifecycleCoroutineScope, collector: (UiState) -> Unit) {
@ -60,7 +59,7 @@ class HomeViewModel @Inject internal constructor(
}
uiState.emit(UiState.Normal(updated))
} catch (e: Exception) {
uiState.emit(UiState.Error(e.message))
uiState.emit(UiState.Error(e))
}
}
}

View file

@ -5,7 +5,6 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import dev.jdtech.jellyfin.repository.JellyfinRepository
import dev.jdtech.jellyfin.utils.SortBy
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import org.jellyfin.sdk.model.api.BaseItemDto
import org.jellyfin.sdk.model.api.SortOrder
@ -24,7 +23,7 @@ constructor(
sealed class UiState {
data class Normal(val items: List<BaseItemDto>) : UiState()
object Loading : UiState()
data class Error(val message: String?) : UiState()
data class Error(val error: Exception) : UiState()
}
fun onUiState(scope: LifecycleCoroutineScope, collector: (UiState) -> Unit) {
@ -55,7 +54,7 @@ constructor(
)
uiState.emit(UiState.Normal(items))
} catch (e: Exception) {
uiState.emit(UiState.Error(e.message))
uiState.emit(UiState.Error(e))
}
}
}

View file

@ -12,7 +12,6 @@ import dev.jdtech.jellyfin.database.ServerDatabaseDao
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.jellyfin.sdk.model.api.AuthenticateUserByName

View file

@ -19,10 +19,6 @@ class MainViewModel
constructor(
private val database: ServerDatabaseDao,
) : ViewModel() {
private val _doneLoading = MutableLiveData<Boolean>()
val doneLoading: LiveData<Boolean> = _doneLoading
private val _navigateToAddServer = MutableLiveData<Boolean>()
val navigateToAddServer: LiveData<Boolean> = _navigateToAddServer
@ -36,9 +32,7 @@ constructor(
if (servers.isEmpty()) {
_navigateToAddServer.value = true
}
_doneLoading.value = true
}
_doneLoading.value = true
}
fun doneNavigateToAddServer() {

View file

@ -52,7 +52,7 @@ constructor(
) : UiState()
object Loading : UiState()
data class Error(val message: String?) : UiState()
data class Error(val error: Exception) : UiState()
}
fun onUiState(scope: LifecycleCoroutineScope, collector: (UiState) -> Unit) {
@ -121,9 +121,7 @@ constructor(
)
)
} catch (e: Exception) {
Timber.d(e)
Timber.d(itemId.toString())
uiState.emit(UiState.Error(e.message))
uiState.emit(UiState.Error(e))
}
}
}

View file

@ -5,7 +5,6 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import dev.jdtech.jellyfin.models.CollectionType
import dev.jdtech.jellyfin.repository.JellyfinRepository
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import org.jellyfin.sdk.model.api.BaseItemDto
import javax.inject.Inject
@ -22,7 +21,7 @@ constructor(
sealed class UiState {
data class Normal(val collections: List<BaseItemDto>) : UiState()
object Loading : UiState()
data class Error(val message: String?) : UiState()
data class Error(val error: Exception) : UiState()
}
fun onUiState(scope: LifecycleCoroutineScope, collector: (UiState) -> Unit) {
@ -43,7 +42,7 @@ constructor(
uiState.emit(UiState.Normal(collections))
} catch (e: Exception) {
uiState.emit(
UiState.Error(e.message)
UiState.Error(e)
)
}
}

View file

@ -9,10 +9,8 @@ import dev.jdtech.jellyfin.models.ContentType.TVSHOW
import dev.jdtech.jellyfin.repository.JellyfinRepository
import dev.jdtech.jellyfin.utils.contentType
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import org.jellyfin.sdk.model.api.BaseItemDto
import java.lang.Exception
import java.util.UUID
import javax.inject.Inject
@ -26,7 +24,7 @@ internal class PersonDetailViewModel @Inject internal constructor(
sealed class UiState {
data class Normal(val data: PersonOverview, val starredIn: StarredIn) : UiState()
object Loading : UiState()
data class Error(val message: String?) : UiState()
data class Error(val error: Exception) : UiState()
}
fun onUiState(scope: LifecycleCoroutineScope, collector: (UiState) -> Unit) {
@ -58,7 +56,7 @@ internal class PersonDetailViewModel @Inject internal constructor(
uiState.emit(UiState.Normal(data, starredIn))
} catch (e: Exception) {
uiState.emit(UiState.Error(e.message))
uiState.emit(UiState.Error(e))
}
}
}

View file

@ -59,7 +59,7 @@ class PlayerViewModel @Inject internal constructor(
createItems(item, playbackPosition, mediaSourceIndex).let(::PlayerItems)
} catch (e: Exception) {
Timber.d(e)
PlayerItemError(e.toString())
PlayerItemError(e)
}
playerItems.tryEmit(items)
@ -195,6 +195,6 @@ class PlayerViewModel @Inject internal constructor(
sealed class PlayerItemState
data class PlayerItemError(val message: String) : PlayerItemState()
data class PlayerItemError(val error: Exception) : PlayerItemState()
data class PlayerItems(val items: List<PlayerItem>) : PlayerItemState()
}

View file

@ -8,7 +8,6 @@ import dev.jdtech.jellyfin.models.FavoriteSection
import dev.jdtech.jellyfin.repository.JellyfinRepository
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.*
@ -25,7 +24,7 @@ constructor(
sealed class UiState {
data class Normal(val sections: List<FavoriteSection>) : UiState()
object Loading : UiState()
data class Error(val message: String?) : UiState()
data class Error(val error: Exception) : UiState()
}
fun onUiState(scope: LifecycleCoroutineScope, collector: (UiState) -> Unit) {
@ -74,7 +73,7 @@ constructor(
uiState.emit(UiState.Normal(sections))
} catch (e: Exception) {
uiState.emit(UiState.Error(e.message))
uiState.emit(UiState.Error(e))
}
}
}

View file

@ -5,7 +5,6 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import dev.jdtech.jellyfin.adapters.EpisodeItem
import dev.jdtech.jellyfin.repository.JellyfinRepository
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import org.jellyfin.sdk.model.api.ItemFields
import java.util.*
@ -22,7 +21,7 @@ constructor(
sealed class UiState {
data class Normal(val episodes: List<EpisodeItem>) : UiState()
object Loading : UiState()
data class Error(val message: String?) : UiState()
data class Error(val error: Exception) : UiState()
}
fun onUiState(scope: LifecycleCoroutineScope, collector: (UiState) -> Unit) {
@ -36,7 +35,7 @@ constructor(
val episodes = getEpisodes(seriesId, seasonId)
uiState.emit(UiState.Normal(episodes))
} catch (e: Exception) {
uiState.emit(UiState.Error(e.message))
uiState.emit(UiState.Error(e))
}
}
}

View file

@ -11,9 +11,7 @@ import dev.jdtech.jellyfin.database.ServerDatabaseDao
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.*
import javax.inject.Inject
@ -43,10 +41,8 @@ constructor(
* @param server The server
*/
fun deleteServer(server: Server) {
viewModelScope.launch {
withContext(Dispatchers.IO) {
database.delete(server.id)
}
viewModelScope.launch(Dispatchers.IO) {
database.delete(server.id)
}
}