MovieLocalDataSource.kt
package com.louisfn.somovie.data.database.datasource
import androidx.annotation.AnyThread
import androidx.paging.PagingSource
import androidx.room.withTransaction
import com.louisfn.somovie.core.common.annotation.IoDispatcher
import com.louisfn.somovie.data.database.AppDatabase
import com.louisfn.somovie.data.database.entity.ExploreEntity
import com.louisfn.somovie.data.database.entity.MovieEntity
import com.louisfn.somovie.data.database.entity.MovieGenreCrossRefEntity
import com.louisfn.somovie.data.database.entity.MovieProductionCompanyCrossRefEntity
import com.louisfn.somovie.data.database.relation.MovieWithRelations
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.invoke
import javax.inject.Inject
interface MovieLocalDataSource {
@AnyThread
fun moviesChanges(category: ExploreEntity.Category, limit: Int): Flow<List<MovieEntity>>
@AnyThread
fun movieChanges(movieId: Long): Flow<MovieWithRelations>
@AnyThread
fun getPagingMovies(category: ExploreEntity.Category): PagingSource<Int, MovieEntity>
@AnyThread
suspend fun insertOrIgnoreMovies(category: ExploreEntity.Category, movies: List<MovieEntity>, page: Int)
@AnyThread
suspend fun insertOrUpdateMovie(movieWithRelations: MovieWithRelations)
@AnyThread
suspend fun deleteExploreMovies(category: ExploreEntity.Category)
}
internal class DefaultMovieLocalDataSource @Inject constructor(
private val database: AppDatabase,
@IoDispatcher private val ioDispatcher: CoroutineDispatcher,
) : MovieLocalDataSource {
override fun moviesChanges(category: ExploreEntity.Category, limit: Int): Flow<List<MovieEntity>> =
database.movieDao().changes(category, limit)
override fun movieChanges(movieId: Long): Flow<MovieWithRelations> =
database.movieDao().movieWithRelationChanges(movieId)
override fun getPagingMovies(category: ExploreEntity.Category): PagingSource<Int, MovieEntity> =
database.movieDao().getPaging(category)
override suspend fun insertOrUpdateMovie(movieWithRelations: MovieWithRelations) = ioDispatcher {
val movieId = movieWithRelations.movie.id
with(database) {
withTransaction {
movieDao().insertOrUpdate(movieWithRelations.movie)
genreDao().insertOrIgnore(movieWithRelations.genres)
companyDao().insertOrIgnore(movieWithRelations.productionCompanies)
with(productionCountryDao()) {
delete(movieId)
insertOrAbort(movieWithRelations.productionCountries)
}
with(movieGenreCrossRefDao()) {
delete(movieId)
insertOrAbort(
movieWithRelations.genres.map {
MovieGenreCrossRefEntity(
movieId = movieId,
genreId = it.id,
)
},
)
}
with(movieProductionCompanyCrossRefDao()) {
delete(movieId)
insertOrAbort(
movieWithRelations.productionCompanies.map {
MovieProductionCompanyCrossRefEntity(
movieId = movieId,
companyId = it.id,
)
},
)
}
}
}
}
override suspend fun insertOrIgnoreMovies(category: ExploreEntity.Category, movies: List<MovieEntity>, page: Int) {
with(database) {
withTransaction {
movieDao().insertOrIgnore(movies)
exploreDao().insertOrIgnore(
movies.map {
ExploreEntity(
movieId = it.id,
category = category,
page = page,
)
},
)
}
}
}
override suspend fun deleteExploreMovies(category: ExploreEntity.Category) =
database.exploreDao().delete(category)
}