mirror of
https://github.com/bbara04/Purefin.git
synced 2026-07-23 19:26:50 +00:00
feat: update media selection logic to handle offline state in view models and DTOs
This commit is contained in:
@@ -3,14 +3,15 @@ package hu.bbara.purefin.core.feature.content.episode
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import hu.bbara.purefin.core.Offline
|
||||
import hu.bbara.purefin.core.data.LocalMediaRepository
|
||||
import hu.bbara.purefin.core.download.DownloadState
|
||||
import hu.bbara.purefin.core.download.MediaDownloadController
|
||||
import hu.bbara.purefin.core.navigation.EpisodeDto
|
||||
import hu.bbara.purefin.core.navigation.NavigationManager
|
||||
import hu.bbara.purefin.core.navigation.Route
|
||||
import hu.bbara.purefin.core.navigation.SeriesDto
|
||||
import hu.bbara.purefin.model.Episode
|
||||
import java.util.UUID
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
@@ -25,28 +26,32 @@ import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class EpisodeScreenViewModel @Inject constructor(
|
||||
private val mediaCatalogReader: LocalMediaRepository,
|
||||
private val defaultMediaCatalogReader: LocalMediaRepository,
|
||||
@param:Offline private val offlineMediaCatalogReader: LocalMediaRepository,
|
||||
private val navigationManager: NavigationManager,
|
||||
private val mediaDownloadManager: MediaDownloadController,
|
||||
): ViewModel() {
|
||||
|
||||
private val _episodeId = MutableStateFlow<UUID?>(null)
|
||||
private val _seriesId = MutableStateFlow<UUID?>(null)
|
||||
private val _episode = MutableStateFlow<EpisodeDto?>(null)
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
val episode: StateFlow<Episode?> = _episodeId
|
||||
.flatMapLatest { episodeId ->
|
||||
if (episodeId == null) flowOf(null) else mediaCatalogReader.getEpisode(episodeId)
|
||||
val episode: StateFlow<Episode?> = _episode
|
||||
.flatMapLatest { episode ->
|
||||
if (episode == null) {
|
||||
flowOf(null)
|
||||
} else {
|
||||
mediaCatalogReader(episode.offline).getEpisode(episode.id)
|
||||
}
|
||||
}
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null)
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
val seriesTitle: StateFlow<String?> = _seriesId
|
||||
.flatMapLatest { seriesId ->
|
||||
if (seriesId == null) {
|
||||
val seriesTitle: StateFlow<String?> = _episode
|
||||
.flatMapLatest { episode ->
|
||||
if (episode == null) {
|
||||
flowOf(null)
|
||||
} else {
|
||||
mediaCatalogReader.getSeries(seriesId).map { it?.name }
|
||||
mediaCatalogReader(episode.offline).getSeries(episode.seriesId).map { it?.name }
|
||||
}
|
||||
}
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null)
|
||||
@@ -59,22 +64,25 @@ class EpisodeScreenViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
fun onSeriesClick() {
|
||||
val seriesId = _seriesId.value ?: return
|
||||
navigationManager.navigate(Route.SeriesRoute(SeriesDto(id = seriesId)))
|
||||
val episode = _episode.value ?: return
|
||||
navigationManager.navigate(
|
||||
Route.SeriesRoute(
|
||||
SeriesDto(id = episode.seriesId, offline = episode.offline)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun selectEpisode(seriesId: UUID, seasonId: UUID, episodeId: UUID) {
|
||||
_episodeId.value = episodeId
|
||||
_seriesId.value = seriesId
|
||||
fun selectEpisode(episode: EpisodeDto) {
|
||||
_episode.value = episode
|
||||
viewModelScope.launch {
|
||||
mediaDownloadManager.observeDownloadState(episodeId.toString()).collect {
|
||||
mediaDownloadManager.observeDownloadState(episode.id.toString()).collect {
|
||||
_downloadState.value = it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onDownloadClick() {
|
||||
val episodeId = _episodeId.value ?: return
|
||||
val episodeId = _episode.value?.id ?: return
|
||||
viewModelScope.launch {
|
||||
when (_downloadState.value) {
|
||||
is DownloadState.NotDownloaded, is DownloadState.Failed -> {
|
||||
@@ -87,4 +95,7 @@ class EpisodeScreenViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun mediaCatalogReader(offline: Boolean): LocalMediaRepository {
|
||||
return if (offline) offlineMediaCatalogReader else defaultMediaCatalogReader
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,13 +3,14 @@ package hu.bbara.purefin.core.feature.content.movie
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import hu.bbara.purefin.core.Offline
|
||||
import hu.bbara.purefin.core.data.LocalMediaRepository
|
||||
import hu.bbara.purefin.core.download.DownloadState
|
||||
import hu.bbara.purefin.core.download.MediaDownloadController
|
||||
import hu.bbara.purefin.core.navigation.MovieDto
|
||||
import hu.bbara.purefin.core.navigation.NavigationManager
|
||||
import hu.bbara.purefin.core.navigation.Route
|
||||
import hu.bbara.purefin.model.Movie
|
||||
import java.util.UUID
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
@@ -23,17 +24,22 @@ import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class MovieScreenViewModel @Inject constructor(
|
||||
private val mediaCatalogReader: LocalMediaRepository,
|
||||
private val defaultMediaCatalogReader: LocalMediaRepository,
|
||||
@param:Offline private val offlineMediaCatalogReader: LocalMediaRepository,
|
||||
private val navigationManager: NavigationManager,
|
||||
private val mediaDownloadManager: MediaDownloadController,
|
||||
): ViewModel() {
|
||||
|
||||
private val _movieId = MutableStateFlow<UUID?>(null)
|
||||
private val _movie = MutableStateFlow<MovieDto?>(null)
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
val movie: StateFlow<Movie?> = _movieId
|
||||
.flatMapLatest { movieId ->
|
||||
if (movieId == null) flowOf(null) else mediaCatalogReader.getMovie(movieId)
|
||||
val movie: StateFlow<Movie?> = _movie
|
||||
.flatMapLatest { movie ->
|
||||
if (movie == null) {
|
||||
flowOf(null)
|
||||
} else {
|
||||
mediaCatalogReader(movie.offline).getMovie(movie.id)
|
||||
}
|
||||
}
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null)
|
||||
|
||||
@@ -53,10 +59,10 @@ class MovieScreenViewModel @Inject constructor(
|
||||
navigationManager.replaceAll(Route.Home)
|
||||
}
|
||||
|
||||
fun selectMovie(movieId: UUID) {
|
||||
_movieId.value = movieId
|
||||
fun selectMovie(movie: MovieDto) {
|
||||
_movie.value = movie
|
||||
viewModelScope.launch {
|
||||
mediaDownloadManager.observeDownloadState(movieId.toString()).collect {
|
||||
mediaDownloadManager.observeDownloadState(movie.id.toString()).collect {
|
||||
_downloadState.value = it
|
||||
}
|
||||
}
|
||||
@@ -79,4 +85,7 @@ class MovieScreenViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun mediaCatalogReader(offline: Boolean): LocalMediaRepository {
|
||||
return if (offline) offlineMediaCatalogReader else defaultMediaCatalogReader
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,14 @@ package hu.bbara.purefin.core.feature.content.series
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import hu.bbara.purefin.core.Offline
|
||||
import hu.bbara.purefin.core.data.LocalMediaRepository
|
||||
import hu.bbara.purefin.core.download.DownloadState
|
||||
import hu.bbara.purefin.core.download.MediaDownloadController
|
||||
import hu.bbara.purefin.core.navigation.EpisodeDto
|
||||
import hu.bbara.purefin.core.navigation.NavigationManager
|
||||
import hu.bbara.purefin.core.navigation.Route
|
||||
import hu.bbara.purefin.core.navigation.SeriesDto
|
||||
import hu.bbara.purefin.model.Episode
|
||||
import hu.bbara.purefin.model.Series
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
@@ -27,17 +29,22 @@ import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class SeriesViewModel @Inject constructor(
|
||||
private val mediaCatalogReader: LocalMediaRepository,
|
||||
private val defaultMediaCatalogReader: LocalMediaRepository,
|
||||
@param:Offline private val offlineMediaCatalogReader: LocalMediaRepository,
|
||||
private val navigationManager: NavigationManager,
|
||||
private val mediaDownloadManager: MediaDownloadController,
|
||||
) : ViewModel() {
|
||||
|
||||
private val _seriesId = MutableStateFlow<UUID?>(null)
|
||||
private val _series = MutableStateFlow<SeriesDto?>(null)
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
val series: StateFlow<Series?> = _seriesId
|
||||
.flatMapLatest { seriesId ->
|
||||
if (seriesId == null) flowOf(null) else mediaCatalogReader.getSeries(seriesId)
|
||||
val series: StateFlow<Series?> = _series
|
||||
.flatMapLatest { series ->
|
||||
if (series == null) {
|
||||
flowOf(null)
|
||||
} else {
|
||||
mediaCatalogReader(series.offline).getSeries(series.id)
|
||||
}
|
||||
}
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null)
|
||||
|
||||
@@ -80,12 +87,13 @@ class SeriesViewModel @Inject constructor(
|
||||
|
||||
fun loadSeasonEpisodes(seriesId: UUID, seasonId: UUID) {
|
||||
viewModelScope.launch {
|
||||
mediaCatalogReader.loadSeasonEpisodes(seriesId, seasonId)
|
||||
selectedMediaCatalogReader().loadSeasonEpisodes(seriesId, seasonId)
|
||||
}
|
||||
}
|
||||
|
||||
fun downloadSeason(seriesId: UUID, seasonId: UUID) {
|
||||
viewModelScope.launch {
|
||||
val mediaCatalogReader = selectedMediaCatalogReader()
|
||||
mediaCatalogReader.loadSeasonEpisodes(seriesId, seasonId)
|
||||
val episodes = mediaCatalogReader.getSeries(seriesId)
|
||||
.first()
|
||||
@@ -105,6 +113,7 @@ class SeriesViewModel @Inject constructor(
|
||||
|
||||
fun downloadSeries(seriesData: Series) {
|
||||
viewModelScope.launch {
|
||||
val mediaCatalogReader = selectedMediaCatalogReader()
|
||||
seriesData.seasons.forEach { season ->
|
||||
mediaCatalogReader.loadSeasonEpisodes(seriesData.id, season.id)
|
||||
}
|
||||
@@ -136,7 +145,8 @@ class SeriesViewModel @Inject constructor(
|
||||
EpisodeDto(
|
||||
id = episodeId,
|
||||
seasonId = seasonId,
|
||||
seriesId = seriesId
|
||||
seriesId = seriesId,
|
||||
offline = _series.value?.offline == true,
|
||||
)
|
||||
))
|
||||
}
|
||||
@@ -153,10 +163,18 @@ class SeriesViewModel @Inject constructor(
|
||||
navigationManager.replaceAll(Route.Home)
|
||||
}
|
||||
|
||||
fun selectSeries(seriesId: UUID) {
|
||||
_seriesId.value = seriesId
|
||||
fun selectSeries(series: SeriesDto) {
|
||||
_series.value = series
|
||||
viewModelScope.launch {
|
||||
mediaCatalogReader.loadSeasons(seriesId)
|
||||
mediaCatalogReader(series.offline).loadSeasons(series.id)
|
||||
}
|
||||
}
|
||||
|
||||
private fun selectedMediaCatalogReader(): LocalMediaRepository {
|
||||
return mediaCatalogReader(_series.value?.offline == true)
|
||||
}
|
||||
|
||||
private fun mediaCatalogReader(offline: Boolean): LocalMediaRepository {
|
||||
return if (offline) offlineMediaCatalogReader else defaultMediaCatalogReader
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class DownloadsViewModel @Inject constructor(
|
||||
@Offline private val offlineCatalogReader: LocalMediaRepository,
|
||||
@param:Offline private val offlineCatalogReader: LocalMediaRepository,
|
||||
private val navigationManager: NavigationManager,
|
||||
private val downloadManager: MediaDownloadController,
|
||||
) : ViewModel() {
|
||||
@@ -31,7 +31,7 @@ class DownloadsViewModel @Inject constructor(
|
||||
fun onMovieSelected(movieId: UUID) {
|
||||
navigationManager.navigate(
|
||||
Route.MovieRoute(
|
||||
MovieDto(id = movieId)
|
||||
MovieDto(id = movieId, offline = true)
|
||||
))
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ class DownloadsViewModel @Inject constructor(
|
||||
viewModelScope.launch {
|
||||
navigationManager.navigate(
|
||||
Route.SeriesRoute(
|
||||
SeriesDto(id = seriesId)
|
||||
SeriesDto(id = seriesId, offline = true)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,4 +11,5 @@ data class EpisodeDto(
|
||||
val seasonId: UUID,
|
||||
@Serializable(with = UuidSerializer::class)
|
||||
val seriesId: UUID,
|
||||
val offline: Boolean = false,
|
||||
)
|
||||
|
||||
@@ -7,4 +7,5 @@ import kotlinx.serialization.Serializable
|
||||
data class MovieDto(
|
||||
@Serializable(with = UuidSerializer::class)
|
||||
val id: UUID,
|
||||
val offline: Boolean = false,
|
||||
)
|
||||
|
||||
@@ -7,4 +7,5 @@ import kotlinx.serialization.Serializable
|
||||
data class SeriesDto(
|
||||
@Serializable(with = UuidSerializer::class)
|
||||
val id: UUID,
|
||||
val offline: Boolean = false,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user