package com.streamflixreborn.streamflix.fragments.movies import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.streamflixreborn.streamflix.database.AppDatabase import com.streamflixreborn.streamflix.models.Movie import com.streamflixreborn.streamflix.utils.ParentalControlUtils import com.streamflixreborn.streamflix.utils.UserPreferences import com.streamflixreborn.streamflix.utils.ProviderChangeNotifier import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.transformLatest import kotlinx.coroutines.launch class MoviesViewModel(database: AppDatabase) : ViewModel() { private val _state = MutableStateFlow(State.Loading) init { // Listen for provider changes and reload data viewModelScope.launch { ProviderChangeNotifier.providerChangeFlow.collect { getMovies() } } } @OptIn(ExperimentalCoroutinesApi::class) val state: Flow = combine( _state, _state.transformLatest { state -> when (state) { is State.SuccessLoading -> { if (state.movies.isEmpty()) { emit(emptyList()) } else { emitAll(database.movieDao().getByIds(state.movies.map { it.id })) } } else -> emit(emptyList()) } }, ) { state, moviesDb -> when (state) { is State.SuccessLoading -> { val moviesById = moviesDb.associateBy { it.id } State.SuccessLoading( movies = state.movies.map { movie -> moviesById[movie.id] ?.takeIf { !movie.isSame(it) } ?.let { movie.copy().merge(it) } ?: movie }, hasMore = state.hasMore ) } else -> state } }.flowOn(Dispatchers.IO) private var page = 1 sealed class State { data object Loading : State() data object LoadingMore : State() data class SuccessLoading(val movies: List, val hasMore: Boolean) : State() data class FailedLoading(val error: Exception) : State() } init { getMovies() } fun getMovies() = viewModelScope.launch(Dispatchers.IO) { _state.emit(State.Loading) try { val movies = ParentalControlUtils.filterItems( UserPreferences.currentProvider!!.getMovies() ).filterIsInstance() page = 1 _state.emit(State.SuccessLoading(movies, true)) } catch (e: Exception) { Log.e("MoviesViewModel", "getMovies: ", e) _state.emit(State.FailedLoading(e)) } } fun loadMoreMovies() = viewModelScope.launch(Dispatchers.IO) { val currentState = _state.value if (currentState is State.SuccessLoading) { _state.emit(State.LoadingMore) try { val movies = ParentalControlUtils.filterItems( UserPreferences.currentProvider!!.getMovies(page + 1) ).filterIsInstance() page += 1 _state.emit( State.SuccessLoading( movies = currentState.movies + movies, hasMore = movies.isNotEmpty(), ) ) } catch (e: Exception) { Log.e("MoviesViewModel", "loadMoreMovies: ", e) _state.emit(State.FailedLoading(e)) } } } }