refactor: replace SharedFlows with Channels for sending events

This commit is contained in:
Jarne Demeulemeester 2023-11-06 23:42:00 +01:00
parent 6c3360f8e7
commit 218b4f1af4
No known key found for this signature in database
GPG key ID: 1E5C6AFBD622E9F5
22 changed files with 170 additions and 116 deletions

View file

@ -42,6 +42,7 @@ import dev.jdtech.jellyfin.mpv.TrackType
import dev.jdtech.jellyfin.utils.PlayerGestureHelper import dev.jdtech.jellyfin.utils.PlayerGestureHelper
import dev.jdtech.jellyfin.utils.PreviewScrubListener import dev.jdtech.jellyfin.utils.PreviewScrubListener
import dev.jdtech.jellyfin.viewmodels.PlayerActivityViewModel import dev.jdtech.jellyfin.viewmodels.PlayerActivityViewModel
import dev.jdtech.jellyfin.viewmodels.PlayerEvents
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -169,8 +170,10 @@ class PlayerActivity : BasePlayerActivity() {
} }
launch { launch {
viewModel.navigateBack.collect { viewModel.eventsChannelFlow.collect { event ->
if (it) finish() when (event) {
is PlayerEvents.NavigateBack -> finish()
}
} }
} }
} }

View file

@ -17,6 +17,7 @@ import androidx.navigation.fragment.findNavController
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import dev.jdtech.jellyfin.adapters.DiscoveredServerListAdapter import dev.jdtech.jellyfin.adapters.DiscoveredServerListAdapter
import dev.jdtech.jellyfin.databinding.FragmentAddServerBinding import dev.jdtech.jellyfin.databinding.FragmentAddServerBinding
import dev.jdtech.jellyfin.viewmodels.AddServerEvent
import dev.jdtech.jellyfin.viewmodels.AddServerViewModel import dev.jdtech.jellyfin.viewmodels.AddServerViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
@ -81,9 +82,9 @@ class AddServerFragment : Fragment() {
viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.navigateToLogin.collect { viewModel.eventsChannelFlow.collect { event ->
if (it) { when (event) {
navigateToLoginFragment() is AddServerEvent.NavigateToLogin -> navigateToLoginFragment()
} }
} }
} }

View file

@ -20,6 +20,7 @@ import dev.jdtech.jellyfin.models.FindroidItem
import dev.jdtech.jellyfin.models.FindroidMovie import dev.jdtech.jellyfin.models.FindroidMovie
import dev.jdtech.jellyfin.models.FindroidShow import dev.jdtech.jellyfin.models.FindroidShow
import dev.jdtech.jellyfin.utils.restart import dev.jdtech.jellyfin.utils.restart
import dev.jdtech.jellyfin.viewmodels.DownloadsEvent
import dev.jdtech.jellyfin.viewmodels.DownloadsViewModel import dev.jdtech.jellyfin.viewmodels.DownloadsViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
@ -48,14 +49,18 @@ class DownloadsFragment : Fragment() {
viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
launch { launch {
viewModel.connectionError.collect { viewModel.eventsChannelFlow.collect { event ->
Snackbar.make(binding.root, CoreR.string.no_server_connection, Snackbar.LENGTH_INDEFINITE) when (event) {
.setTextMaxLines(2) is DownloadsEvent.ConnectionError -> {
.setAction(CoreR.string.offline_mode) { Snackbar.make(binding.root, CoreR.string.no_server_connection, Snackbar.LENGTH_INDEFINITE)
appPreferences.offlineMode = true .setTextMaxLines(2)
activity?.restart() .setAction(CoreR.string.offline_mode) {
appPreferences.offlineMode = true
activity?.restart()
}
.show()
} }
.show() }
} }
} }
launch { launch {

View file

@ -33,6 +33,7 @@ import dev.jdtech.jellyfin.models.UiText
import dev.jdtech.jellyfin.models.isDownloaded import dev.jdtech.jellyfin.models.isDownloaded
import dev.jdtech.jellyfin.models.isDownloading import dev.jdtech.jellyfin.models.isDownloading
import dev.jdtech.jellyfin.utils.setIconTintColorAttribute import dev.jdtech.jellyfin.utils.setIconTintColorAttribute
import dev.jdtech.jellyfin.viewmodels.EpisodeBottomSheetEvent
import dev.jdtech.jellyfin.viewmodels.EpisodeBottomSheetViewModel import dev.jdtech.jellyfin.viewmodels.EpisodeBottomSheetViewModel
import dev.jdtech.jellyfin.viewmodels.PlayerViewModel import dev.jdtech.jellyfin.viewmodels.PlayerViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -122,14 +123,11 @@ class EpisodeBottomSheetFragment : BottomSheetDialogFragment() {
} }
launch { launch {
viewModel.downloadError.collect { uiText -> viewModel.eventsChannelFlow.collect { event ->
createErrorDialog(uiText) when (event) {
} is EpisodeBottomSheetEvent.NavigateBack -> findNavController().navigateUp()
} is EpisodeBottomSheetEvent.DownloadError -> createErrorDialog(event.uiText)
}
launch {
viewModel.navigateBack.collect {
if (it) findNavController().navigateUp()
} }
} }
} }

View file

@ -19,6 +19,7 @@ import dev.jdtech.jellyfin.AppPreferences
import dev.jdtech.jellyfin.adapters.UserLoginListAdapter import dev.jdtech.jellyfin.adapters.UserLoginListAdapter
import dev.jdtech.jellyfin.database.ServerDatabaseDao import dev.jdtech.jellyfin.database.ServerDatabaseDao
import dev.jdtech.jellyfin.databinding.FragmentLoginBinding import dev.jdtech.jellyfin.databinding.FragmentLoginBinding
import dev.jdtech.jellyfin.viewmodels.LoginEvent
import dev.jdtech.jellyfin.viewmodels.LoginViewModel import dev.jdtech.jellyfin.viewmodels.LoginViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
@ -123,9 +124,9 @@ class LoginFragment : Fragment() {
viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.navigateToMain.collect { viewModel.eventsChannelFlow.collect { event ->
if (it) { when (event) {
navigateToHomeFragment() is LoginEvent.NavigateToHome -> navigateToHomeFragment()
} }
} }
} }

View file

@ -37,6 +37,7 @@ import dev.jdtech.jellyfin.models.isDownloaded
import dev.jdtech.jellyfin.models.isDownloading import dev.jdtech.jellyfin.models.isDownloading
import dev.jdtech.jellyfin.utils.checkIfLoginRequired import dev.jdtech.jellyfin.utils.checkIfLoginRequired
import dev.jdtech.jellyfin.utils.setIconTintColorAttribute import dev.jdtech.jellyfin.utils.setIconTintColorAttribute
import dev.jdtech.jellyfin.viewmodels.MovieEvent
import dev.jdtech.jellyfin.viewmodels.MovieViewModel import dev.jdtech.jellyfin.viewmodels.MovieViewModel
import dev.jdtech.jellyfin.viewmodels.PlayerViewModel import dev.jdtech.jellyfin.viewmodels.PlayerViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -118,14 +119,11 @@ class MovieFragment : Fragment() {
} }
launch { launch {
viewModel.downloadError.collect { uiText -> viewModel.eventsChannelFlow.collect { event ->
createErrorDialog(uiText) when (event) {
} is MovieEvent.NavigateBack -> findNavController().navigateUp()
} is MovieEvent.DownloadError -> createErrorDialog(event.uiText)
}
launch {
viewModel.navigateBack.collect {
if (it) findNavController().navigateUp()
} }
} }
} }

View file

@ -20,6 +20,7 @@ import dev.jdtech.jellyfin.models.FindroidEpisode
import dev.jdtech.jellyfin.models.PlayerItem import dev.jdtech.jellyfin.models.PlayerItem
import dev.jdtech.jellyfin.utils.checkIfLoginRequired import dev.jdtech.jellyfin.utils.checkIfLoginRequired
import dev.jdtech.jellyfin.viewmodels.PlayerViewModel import dev.jdtech.jellyfin.viewmodels.PlayerViewModel
import dev.jdtech.jellyfin.viewmodels.SeasonEvent
import dev.jdtech.jellyfin.viewmodels.SeasonViewModel import dev.jdtech.jellyfin.viewmodels.SeasonViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
@ -60,8 +61,10 @@ class SeasonFragment : Fragment() {
} }
launch { launch {
viewModel.navigateBack.collect { viewModel.eventsChannelFlow.collect { event ->
if (it) findNavController().navigateUp() when (event) {
is SeasonEvent.NavigateBack -> findNavController().navigateUp()
}
} }
} }
} }

View file

@ -16,6 +16,7 @@ import dev.jdtech.jellyfin.adapters.ServerAddressAdapter
import dev.jdtech.jellyfin.databinding.FragmentServerAddressesBinding import dev.jdtech.jellyfin.databinding.FragmentServerAddressesBinding
import dev.jdtech.jellyfin.dialogs.AddServerAddressDialog import dev.jdtech.jellyfin.dialogs.AddServerAddressDialog
import dev.jdtech.jellyfin.dialogs.DeleteServerAddressDialog import dev.jdtech.jellyfin.dialogs.DeleteServerAddressDialog
import dev.jdtech.jellyfin.viewmodels.ServerAddressesEvent
import dev.jdtech.jellyfin.viewmodels.ServerAddressesViewModel import dev.jdtech.jellyfin.viewmodels.ServerAddressesViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
@ -57,9 +58,9 @@ class ServerAddressesFragment : Fragment() {
viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.navigateToMain.collect { viewModel.eventsChannelFlow.collect { event ->
if (it) { when (event) {
navigateToMainActivity() is ServerAddressesEvent.NavigateToHome -> navigateToMainActivity()
} }
} }
} }

View file

@ -14,6 +14,7 @@ import dagger.hilt.android.AndroidEntryPoint
import dev.jdtech.jellyfin.adapters.ServerGridAdapter import dev.jdtech.jellyfin.adapters.ServerGridAdapter
import dev.jdtech.jellyfin.databinding.FragmentServerSelectBinding import dev.jdtech.jellyfin.databinding.FragmentServerSelectBinding
import dev.jdtech.jellyfin.dialogs.DeleteServerDialogFragment import dev.jdtech.jellyfin.dialogs.DeleteServerDialogFragment
import dev.jdtech.jellyfin.viewmodels.ServerSelectEvent
import dev.jdtech.jellyfin.viewmodels.ServerSelectViewModel import dev.jdtech.jellyfin.viewmodels.ServerSelectViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
@ -61,13 +62,11 @@ class ServerSelectFragment : Fragment() {
} }
} }
launch { launch {
viewModel.navigateToMain.collect { viewModel.eventsChannelFlow.collect { event ->
if (it) navigateToMainActivity() when (event) {
} is ServerSelectEvent.NavigateToHome -> navigateToMainActivity()
} is ServerSelectEvent.NavigateToLogin -> navigateToLoginFragment()
launch { }
viewModel.navigateToLogin.collect {
if (it) navigateToLoginFragment()
} }
} }
} }

View file

@ -32,6 +32,7 @@ import dev.jdtech.jellyfin.models.isDownloaded
import dev.jdtech.jellyfin.utils.checkIfLoginRequired import dev.jdtech.jellyfin.utils.checkIfLoginRequired
import dev.jdtech.jellyfin.utils.setIconTintColorAttribute import dev.jdtech.jellyfin.utils.setIconTintColorAttribute
import dev.jdtech.jellyfin.viewmodels.PlayerViewModel import dev.jdtech.jellyfin.viewmodels.PlayerViewModel
import dev.jdtech.jellyfin.viewmodels.ShowEvent
import dev.jdtech.jellyfin.viewmodels.ShowViewModel import dev.jdtech.jellyfin.viewmodels.ShowViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
@ -79,8 +80,10 @@ class ShowFragment : Fragment() {
} }
launch { launch {
viewModel.navigateBack.collect { viewModel.eventsChannelFlow.collect { event ->
if (it) findNavController().navigateUp() when (event) {
is ShowEvent.NavigateBack -> findNavController().navigateUp()
}
} }
} }
} }

View file

@ -16,6 +16,7 @@ import dev.jdtech.jellyfin.AppNavigationDirections
import dev.jdtech.jellyfin.adapters.UserListAdapter import dev.jdtech.jellyfin.adapters.UserListAdapter
import dev.jdtech.jellyfin.databinding.FragmentUsersBinding import dev.jdtech.jellyfin.databinding.FragmentUsersBinding
import dev.jdtech.jellyfin.dialogs.DeleteUserDialogFragment import dev.jdtech.jellyfin.dialogs.DeleteUserDialogFragment
import dev.jdtech.jellyfin.viewmodels.UsersEvent
import dev.jdtech.jellyfin.viewmodels.UsersViewModel import dev.jdtech.jellyfin.viewmodels.UsersViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
@ -54,9 +55,9 @@ class UsersFragment : Fragment() {
viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.navigateToMain.collect { viewModel.eventsChannelFlow.collect { event ->
if (it) { when (event) {
navigateToMainActivity() is UsersEvent.NavigateToHome -> navigateToMainActivity()
} }
} }
} }

View file

@ -15,10 +15,10 @@ import dev.jdtech.jellyfin.models.ServerAddress
import dev.jdtech.jellyfin.models.UiText import dev.jdtech.jellyfin.models.UiText
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.jellyfin.sdk.discovery.RecommendedServerInfo import org.jellyfin.sdk.discovery.RecommendedServerInfo
@ -38,11 +38,12 @@ constructor(
) : ViewModel() { ) : ViewModel() {
private val _uiState = MutableStateFlow<UiState>(UiState.Normal) private val _uiState = MutableStateFlow<UiState>(UiState.Normal)
val uiState = _uiState.asStateFlow() val uiState = _uiState.asStateFlow()
private val _navigateToLogin = MutableSharedFlow<Boolean>()
val navigateToLogin = _navigateToLogin.asSharedFlow()
private val _discoveredServersState = MutableStateFlow<DiscoveredServersState>(DiscoveredServersState.Loading) private val _discoveredServersState = MutableStateFlow<DiscoveredServersState>(DiscoveredServersState.Loading)
val discoveredServersState = _discoveredServersState.asStateFlow() val discoveredServersState = _discoveredServersState.asStateFlow()
private val eventsChannel = Channel<AddServerEvent>()
val eventsChannelFlow = eventsChannel.receiveAsFlow()
private val discoveredServers = mutableListOf<DiscoveredServer>() private val discoveredServers = mutableListOf<DiscoveredServer>()
private var serverFound = false private var serverFound = false
@ -206,7 +207,7 @@ constructor(
} }
_uiState.emit(UiState.Normal) _uiState.emit(UiState.Normal)
_navigateToLogin.emit(true) eventsChannel.send(AddServerEvent.NavigateToLogin)
} }
/** /**
@ -269,3 +270,7 @@ constructor(
} }
} }
} }
sealed interface AddServerEvent {
data object NavigateToLogin : AddServerEvent
}

View file

@ -11,11 +11,11 @@ import dev.jdtech.jellyfin.models.FindroidMovie
import dev.jdtech.jellyfin.models.FindroidShow import dev.jdtech.jellyfin.models.FindroidShow
import dev.jdtech.jellyfin.models.UiText import dev.jdtech.jellyfin.models.UiText
import dev.jdtech.jellyfin.repository.JellyfinRepository import dev.jdtech.jellyfin.repository.JellyfinRepository
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
@ -28,8 +28,9 @@ constructor(
) : ViewModel() { ) : ViewModel() {
private val _uiState = MutableStateFlow<UiState>(UiState.Loading) private val _uiState = MutableStateFlow<UiState>(UiState.Loading)
val uiState = _uiState.asStateFlow() val uiState = _uiState.asStateFlow()
private val _connectionError = MutableSharedFlow<Exception>()
val connectionError = _connectionError.asSharedFlow() private val eventsChannel = Channel<DownloadsEvent>()
val eventsChannelFlow = eventsChannel.receiveAsFlow()
sealed class UiState { sealed class UiState {
data class Normal(val sections: List<FavoriteSection>) : UiState() data class Normal(val sections: List<FavoriteSection>) : UiState()
@ -49,7 +50,7 @@ constructor(
// Give the UI a chance to load // Give the UI a chance to load
delay(100) delay(100)
} catch (e: Exception) { } catch (e: Exception) {
_connectionError.emit(e) eventsChannel.send(DownloadsEvent.ConnectionError(e))
} }
} }
} }
@ -88,3 +89,7 @@ constructor(
} }
} }
} }
sealed interface DownloadsEvent {
data class ConnectionError(val error: Exception) : DownloadsEvent
}

View file

@ -13,10 +13,10 @@ import dev.jdtech.jellyfin.models.UiText
import dev.jdtech.jellyfin.models.isDownloading import dev.jdtech.jellyfin.models.isDownloading
import dev.jdtech.jellyfin.repository.JellyfinRepository import dev.jdtech.jellyfin.repository.JellyfinRepository
import dev.jdtech.jellyfin.utils.Downloader import dev.jdtech.jellyfin.utils.Downloader
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.io.File import java.io.File
import java.util.UUID import java.util.UUID
@ -37,11 +37,8 @@ constructor(
private val _downloadStatus = MutableStateFlow(Pair(0, 0)) private val _downloadStatus = MutableStateFlow(Pair(0, 0))
val downloadStatus = _downloadStatus.asStateFlow() val downloadStatus = _downloadStatus.asStateFlow()
private val _downloadError = MutableSharedFlow<UiText>() private val eventsChannel = Channel<EpisodeBottomSheetEvent>()
val downloadError = _downloadError.asSharedFlow() val eventsChannelFlow = eventsChannel.receiveAsFlow()
private val _navigateBack = MutableSharedFlow<Boolean>()
val navigateBack = _navigateBack.asSharedFlow()
private val handler = Handler(Looper.getMainLooper()) private val handler = Handler(Looper.getMainLooper())
@ -75,7 +72,7 @@ constructor(
) )
} catch (_: NullPointerException) { } catch (_: NullPointerException) {
// Navigate back because item does not exist (probably because it's been deleted) // Navigate back because item does not exist (probably because it's been deleted)
_navigateBack.emit(true) eventsChannel.send(EpisodeBottomSheetEvent.NavigateBack)
} catch (e: Exception) { } catch (e: Exception) {
_uiState.emit(UiState.Error(e)) _uiState.emit(UiState.Error(e))
} }
@ -133,7 +130,7 @@ constructor(
_downloadStatus.emit(Pair(10, Random.nextInt())) _downloadStatus.emit(Pair(10, Random.nextInt()))
if (result.second != null) { if (result.second != null) {
_downloadError.emit(result.second!!) eventsChannel.send(EpisodeBottomSheetEvent.DownloadError(result.second!!))
} }
loadEpisode(item.id) loadEpisode(item.id)
@ -188,3 +185,8 @@ constructor(
handler.removeCallbacksAndMessages(null) handler.removeCallbacksAndMessages(null)
} }
} }
sealed interface EpisodeBottomSheetEvent {
data object NavigateBack : EpisodeBottomSheetEvent
data class DownloadError(val uiText: UiText) : EpisodeBottomSheetEvent
}

View file

@ -11,11 +11,11 @@ import dev.jdtech.jellyfin.models.UiText
import dev.jdtech.jellyfin.models.User import dev.jdtech.jellyfin.models.User
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.jellyfin.sdk.api.client.extensions.authenticateWithQuickConnect import org.jellyfin.sdk.api.client.extensions.authenticateWithQuickConnect
@ -38,8 +38,9 @@ constructor(
val usersState = _usersState.asStateFlow() val usersState = _usersState.asStateFlow()
private val _quickConnectUiState = MutableStateFlow<QuickConnectUiState>(QuickConnectUiState.Disabled) private val _quickConnectUiState = MutableStateFlow<QuickConnectUiState>(QuickConnectUiState.Disabled)
val quickConnectUiState = _quickConnectUiState.asStateFlow() val quickConnectUiState = _quickConnectUiState.asStateFlow()
private val _navigateToMain = MutableSharedFlow<Boolean>()
val navigateToMain = _navigateToMain.asSharedFlow() private val eventsChannel = Channel<LoginEvent>()
val eventsChannelFlow = eventsChannel.receiveAsFlow()
private var quickConnectJob: Job? = null private var quickConnectJob: Job? = null
@ -121,7 +122,7 @@ constructor(
saveAuthenticationResult(authenticationResult) saveAuthenticationResult(authenticationResult)
_uiState.emit(UiState.Normal) _uiState.emit(UiState.Normal)
_navigateToMain.emit(true) eventsChannel.send(LoginEvent.NavigateToHome)
} catch (e: Exception) { } catch (e: Exception) {
val message = val message =
if (e.message?.contains("401") == true) { if (e.message?.contains("401") == true) {
@ -157,7 +158,7 @@ constructor(
saveAuthenticationResult(authenticationResult) saveAuthenticationResult(authenticationResult)
_quickConnectUiState.emit(QuickConnectUiState.Normal) _quickConnectUiState.emit(QuickConnectUiState.Normal)
_navigateToMain.emit(true) eventsChannel.send(LoginEvent.NavigateToHome)
} catch (_: Exception) { } catch (_: Exception) {
_quickConnectUiState.emit(QuickConnectUiState.Normal) _quickConnectUiState.emit(QuickConnectUiState.Normal)
} }
@ -189,3 +190,7 @@ constructor(
} }
} }
} }
sealed interface LoginEvent {
data object NavigateToHome : LoginEvent
}

View file

@ -21,10 +21,10 @@ import dev.jdtech.jellyfin.repository.JellyfinRepository
import dev.jdtech.jellyfin.utils.Downloader import dev.jdtech.jellyfin.utils.Downloader
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Runnable import kotlinx.coroutines.Runnable
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.jellyfin.sdk.model.api.BaseItemPerson import org.jellyfin.sdk.model.api.BaseItemPerson
@ -49,11 +49,8 @@ constructor(
private val _downloadStatus = MutableStateFlow(Pair(0, 0)) private val _downloadStatus = MutableStateFlow(Pair(0, 0))
val downloadStatus = _downloadStatus.asStateFlow() val downloadStatus = _downloadStatus.asStateFlow()
private val _downloadError = MutableSharedFlow<UiText>() private val eventsChannel = Channel<MovieEvent>()
val downloadError = _downloadError.asSharedFlow() val eventsChannelFlow = eventsChannel.receiveAsFlow()
private val _navigateBack = MutableSharedFlow<Boolean>()
val navigateBack = _navigateBack.asSharedFlow()
private val handler = Handler(Looper.getMainLooper()) private val handler = Handler(Looper.getMainLooper())
@ -115,7 +112,7 @@ constructor(
) )
} catch (_: NullPointerException) { } catch (_: NullPointerException) {
// Navigate back because item does not exist (probably because it's been deleted) // Navigate back because item does not exist (probably because it's been deleted)
_navigateBack.emit(true) eventsChannel.send(MovieEvent.NavigateBack)
} catch (e: Exception) { } catch (e: Exception) {
_uiState.emit(UiState.Error(e)) _uiState.emit(UiState.Error(e))
} }
@ -330,7 +327,7 @@ constructor(
_downloadStatus.emit(Pair(10, Random.nextInt())) _downloadStatus.emit(Pair(10, Random.nextInt()))
if (result.second != null) { if (result.second != null) {
_downloadError.emit(result.second!!) eventsChannel.send(MovieEvent.DownloadError(result.second!!))
} }
loadData(item.id) loadData(item.id)
@ -385,3 +382,8 @@ constructor(
handler.removeCallbacksAndMessages(null) handler.removeCallbacksAndMessages(null)
} }
} }
sealed interface MovieEvent {
data object NavigateBack : MovieEvent
data class DownloadError(val uiText: UiText) : MovieEvent
}

View file

@ -6,10 +6,10 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import dev.jdtech.jellyfin.models.EpisodeItem import dev.jdtech.jellyfin.models.EpisodeItem
import dev.jdtech.jellyfin.models.FindroidSeason import dev.jdtech.jellyfin.models.FindroidSeason
import dev.jdtech.jellyfin.repository.JellyfinRepository import dev.jdtech.jellyfin.repository.JellyfinRepository
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.jellyfin.sdk.model.api.ItemFields import org.jellyfin.sdk.model.api.ItemFields
import java.util.UUID import java.util.UUID
@ -24,8 +24,8 @@ constructor(
private val _uiState = MutableStateFlow<UiState>(UiState.Loading) private val _uiState = MutableStateFlow<UiState>(UiState.Loading)
val uiState = _uiState.asStateFlow() val uiState = _uiState.asStateFlow()
private val _navigateBack = MutableSharedFlow<Boolean>() private val eventsChannel = Channel<SeasonEvent>()
val navigateBack = _navigateBack.asSharedFlow() val eventsChannelFlow = eventsChannel.receiveAsFlow()
sealed class UiState { sealed class UiState {
data class Normal(val episodes: List<EpisodeItem>) : UiState() data class Normal(val episodes: List<EpisodeItem>) : UiState()
@ -44,7 +44,7 @@ constructor(
_uiState.emit(UiState.Normal(episodes)) _uiState.emit(UiState.Normal(episodes))
} catch (_: NullPointerException) { } catch (_: NullPointerException) {
// Navigate back because item does not exist (probably because it's been deleted) // Navigate back because item does not exist (probably because it's been deleted)
_navigateBack.emit(true) eventsChannel.send(SeasonEvent.NavigateBack)
} catch (e: Exception) { } catch (e: Exception) {
_uiState.emit(UiState.Error(e)) _uiState.emit(UiState.Error(e))
} }
@ -63,3 +63,7 @@ constructor(
return listOf(header) + episodes.map { EpisodeItem.Episode(it) } return listOf(header) + episodes.map { EpisodeItem.Episode(it) }
} }
} }
sealed interface SeasonEvent {
data object NavigateBack : SeasonEvent
}

View file

@ -7,10 +7,10 @@ import dev.jdtech.jellyfin.api.JellyfinApi
import dev.jdtech.jellyfin.database.ServerDatabaseDao import dev.jdtech.jellyfin.database.ServerDatabaseDao
import dev.jdtech.jellyfin.models.ServerAddress import dev.jdtech.jellyfin.models.ServerAddress
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
import java.util.UUID import java.util.UUID
@ -32,8 +32,8 @@ constructor(
data class Error(val error: Exception) : UiState() data class Error(val error: Exception) : UiState()
} }
private val _navigateToMain = MutableSharedFlow<Boolean>() private val eventsChannel = Channel<ServerAddressesEvent>()
val navigateToMain = _navigateToMain.asSharedFlow() val eventsChannelFlow = eventsChannel.receiveAsFlow()
private var currentServerId: String = "" private var currentServerId: String = ""
@ -75,7 +75,7 @@ constructor(
jellyfinApi.api.baseUrl = address.address jellyfinApi.api.baseUrl = address.address
_navigateToMain.emit(true) eventsChannel.send(ServerAddressesEvent.NavigateToHome)
} }
} }
@ -87,3 +87,7 @@ constructor(
} }
} }
} }
sealed interface ServerAddressesEvent {
data object NavigateToHome : ServerAddressesEvent
}

View file

@ -8,10 +8,10 @@ import dev.jdtech.jellyfin.api.JellyfinApi
import dev.jdtech.jellyfin.database.ServerDatabaseDao import dev.jdtech.jellyfin.database.ServerDatabaseDao
import dev.jdtech.jellyfin.models.Server import dev.jdtech.jellyfin.models.Server
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
@ -26,11 +26,8 @@ constructor(
private val _uiState = MutableStateFlow<UiState>(UiState.Loading) private val _uiState = MutableStateFlow<UiState>(UiState.Loading)
val uiState = _uiState.asStateFlow() val uiState = _uiState.asStateFlow()
private val _navigateToMain = MutableSharedFlow<Boolean>() private val eventsChannel = Channel<ServerSelectEvent>()
val navigateToMain = _navigateToMain.asSharedFlow() val eventsChannelFlow = eventsChannel.receiveAsFlow()
private val _navigateToLogin = MutableSharedFlow<Boolean>()
val navigateToLogin = _navigateToLogin.asSharedFlow()
sealed class UiState { sealed class UiState {
data class Normal(val servers: List<Server>) : UiState() data class Normal(val servers: List<Server>) : UiState()
@ -75,7 +72,7 @@ constructor(
userId = null userId = null
} }
appPreferences.currentServer = server.id appPreferences.currentServer = server.id
_navigateToLogin.emit(true) eventsChannel.send(ServerSelectEvent.NavigateToLogin)
return@launch return@launch
} }
@ -87,7 +84,12 @@ constructor(
appPreferences.currentServer = server.id appPreferences.currentServer = server.id
_navigateToMain.emit(true) eventsChannel.send(ServerSelectEvent.NavigateToHome)
} }
} }
} }
sealed interface ServerSelectEvent {
data object NavigateToHome : ServerSelectEvent
data object NavigateToLogin : ServerSelectEvent
}

View file

@ -8,10 +8,10 @@ import dev.jdtech.jellyfin.models.FindroidSeason
import dev.jdtech.jellyfin.models.FindroidShow import dev.jdtech.jellyfin.models.FindroidShow
import dev.jdtech.jellyfin.repository.JellyfinRepository import dev.jdtech.jellyfin.repository.JellyfinRepository
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.jellyfin.sdk.model.api.BaseItemPerson import org.jellyfin.sdk.model.api.BaseItemPerson
@ -27,8 +27,8 @@ constructor(
private val _uiState = MutableStateFlow<UiState>(UiState.Loading) private val _uiState = MutableStateFlow<UiState>(UiState.Loading)
val uiState = _uiState.asStateFlow() val uiState = _uiState.asStateFlow()
private val _navigateBack = MutableSharedFlow<Boolean>() private val eventsChannel = Channel<ShowEvent>()
val navigateBack = _navigateBack.asSharedFlow() val eventsChannelFlow = eventsChannel.receiveAsFlow()
sealed class UiState { sealed class UiState {
data class Normal( data class Normal(
@ -93,7 +93,7 @@ constructor(
) )
} catch (_: NullPointerException) { } catch (_: NullPointerException) {
// Navigate back because item does not exist (probably because it's been deleted) // Navigate back because item does not exist (probably because it's been deleted)
_navigateBack.emit(true) eventsChannel.send(ShowEvent.NavigateBack)
} catch (e: Exception) { } catch (e: Exception) {
_uiState.emit(UiState.Error(e)) _uiState.emit(UiState.Error(e))
} }
@ -189,3 +189,7 @@ constructor(
return dateRange.joinToString(separator = " - ") return dateRange.joinToString(separator = " - ")
} }
} }
sealed interface ShowEvent {
data object NavigateBack : ShowEvent
}

View file

@ -7,10 +7,10 @@ import dev.jdtech.jellyfin.api.JellyfinApi
import dev.jdtech.jellyfin.database.ServerDatabaseDao import dev.jdtech.jellyfin.database.ServerDatabaseDao
import dev.jdtech.jellyfin.models.User import dev.jdtech.jellyfin.models.User
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -31,8 +31,8 @@ constructor(
data class Error(val error: Exception) : UiState() data class Error(val error: Exception) : UiState()
} }
private val _navigateToMain = MutableSharedFlow<Boolean>() private val eventsChannel = Channel<UsersEvent>()
val navigateToMain = _navigateToMain.asSharedFlow() val eventsChannelFlow = eventsChannel.receiveAsFlow()
private var currentServerId: String = "" private var currentServerId: String = ""
@ -77,7 +77,11 @@ constructor(
userId = user.id userId = user.id
} }
_navigateToMain.emit(true) eventsChannel.send(UsersEvent.NavigateToHome)
} }
} }
} }
sealed interface UsersEvent {
data object NavigateToHome : UsersEvent
}

View file

@ -27,11 +27,11 @@ import dev.jdtech.jellyfin.utils.bif.BifUtil
import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -60,8 +60,8 @@ constructor(
) )
val uiState = _uiState.asStateFlow() val uiState = _uiState.asStateFlow()
private val _navigateBack = MutableSharedFlow<Boolean>() private val eventsChannel = Channel<PlayerEvents>()
val navigateBack = _navigateBack.asSharedFlow() val eventsChannelFlow = eventsChannel.receiveAsFlow()
private val intros: MutableMap<UUID, Intro> = mutableMapOf() private val intros: MutableMap<UUID, Intro> = mutableMapOf()
@ -317,7 +317,7 @@ constructor(
} }
ExoPlayer.STATE_ENDED -> { ExoPlayer.STATE_ENDED -> {
stateString = "ExoPlayer.STATE_ENDED -" stateString = "ExoPlayer.STATE_ENDED -"
_navigateBack.tryEmit(true) eventsChannel.trySend(PlayerEvents.NavigateBack)
} }
} }
Timber.d("Changed player state to $stateString") Timber.d("Changed player state to $stateString")
@ -366,3 +366,7 @@ constructor(
} }
} }
} }
sealed interface PlayerEvents {
data object NavigateBack : PlayerEvents
}