diff --git a/core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/Interactor.kt b/core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/Interactor.kt index 08659d4e3..3b31c6b84 100644 --- a/core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/Interactor.kt +++ b/core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/Interactor.kt @@ -8,8 +8,6 @@ class Interactor @Inject constructor( imageInteractor: ImageInteractor, movieInteractor: MovieInteractor, notificationInteractor: NotificationInteractor, - pagingKeyInteractor: PagingKeyInteractor, - searchInteractor: SearchInteractor, settingsInteractor: SettingsInteractor, suggestionInteractor: SuggestionInteractor ): AccountInteractor by accountInteractor, @@ -17,7 +15,5 @@ class Interactor @Inject constructor( ImageInteractor by imageInteractor, MovieInteractor by movieInteractor, NotificationInteractor by notificationInteractor, - PagingKeyInteractor by pagingKeyInteractor, - SearchInteractor by searchInteractor, SettingsInteractor by settingsInteractor, SuggestionInteractor by suggestionInteractor \ No newline at end of file diff --git a/core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/MovieInteractor.kt b/core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/MovieInteractor.kt index ebc4536bf..dfed8de45 100644 --- a/core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/MovieInteractor.kt +++ b/core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/MovieInteractor.kt @@ -5,13 +5,14 @@ import androidx.paging.PagingSource import kotlinx.coroutines.flow.Flow import org.michaelbel.movies.common.list.MovieList import org.michaelbel.movies.network.Either -import org.michaelbel.movies.network.model.MovieResponse import org.michaelbel.movies.persistence.database.entity.MovieDb interface MovieInteractor { fun moviesPagingData(movieList: MovieList): Flow> + fun moviesPagingData(searchQuery: String): Flow> + fun moviesPagingSource(pagingKey: String): PagingSource fun moviesFlow(pagingKey: String, limit: Int): Flow> @@ -24,7 +25,5 @@ interface MovieInteractor { suspend fun removeMovie(pagingKey: String, movieId: Int) - suspend fun insertMovies(pagingKey: String, movies: List) - suspend fun insertMovie(pagingKey: String, movie: MovieDb) } \ No newline at end of file diff --git a/core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/PagingKeyInteractor.kt b/core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/PagingKeyInteractor.kt deleted file mode 100644 index 7edfe7adf..000000000 --- a/core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/PagingKeyInteractor.kt +++ /dev/null @@ -1,10 +0,0 @@ -package org.michaelbel.movies.interactor - -interface PagingKeyInteractor { - - suspend fun page(pagingKey: String): Int? - - suspend fun removePagingKey(pagingKey: String) - - suspend fun insertPagingKey(pagingKey: String, page: Int) -} \ No newline at end of file diff --git a/core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/SearchInteractor.kt b/core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/SearchInteractor.kt deleted file mode 100644 index 055b79377..000000000 --- a/core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/SearchInteractor.kt +++ /dev/null @@ -1,9 +0,0 @@ -package org.michaelbel.movies.interactor - -import org.michaelbel.movies.network.model.MovieResponse -import org.michaelbel.movies.network.model.Result - -interface SearchInteractor { - - suspend fun searchMoviesResult(query: String, page: Int): Result -} \ No newline at end of file diff --git a/core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/di/InteractorModule.kt b/core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/di/InteractorModule.kt index 0739f044a..9f9695fee 100644 --- a/core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/di/InteractorModule.kt +++ b/core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/di/InteractorModule.kt @@ -10,8 +10,6 @@ import org.michaelbel.movies.interactor.AuthenticationInteractor import org.michaelbel.movies.interactor.ImageInteractor import org.michaelbel.movies.interactor.MovieInteractor import org.michaelbel.movies.interactor.NotificationInteractor -import org.michaelbel.movies.interactor.PagingKeyInteractor -import org.michaelbel.movies.interactor.SearchInteractor import org.michaelbel.movies.interactor.SettingsInteractor import org.michaelbel.movies.interactor.SuggestionInteractor import org.michaelbel.movies.interactor.impl.AccountInteractorImpl @@ -19,8 +17,6 @@ import org.michaelbel.movies.interactor.impl.AuthenticationInteractorImpl import org.michaelbel.movies.interactor.impl.ImageInteractorImpl import org.michaelbel.movies.interactor.impl.MovieInteractorImpl import org.michaelbel.movies.interactor.impl.NotificationInteractorImpl -import org.michaelbel.movies.interactor.impl.PagingKeyInteractorImpl -import org.michaelbel.movies.interactor.impl.SearchInteractorImpl import org.michaelbel.movies.interactor.impl.SettingsInteractorImpl import org.michaelbel.movies.interactor.impl.SuggestionInteractorImpl @@ -58,18 +54,6 @@ internal interface InteractorModule { interactor: NotificationInteractorImpl ): NotificationInteractor - @Binds - @Singleton - fun providePagingKeyInteractor( - interactor: PagingKeyInteractorImpl - ): PagingKeyInteractor - - @Binds - @Singleton - fun provideSearchInteractor( - interactor: SearchInteractorImpl - ): SearchInteractor - @Binds @Singleton fun provideSettingsInteractor( diff --git a/core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/impl/MovieInteractorImpl.kt b/core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/impl/MovieInteractorImpl.kt index 4576db9db..1b534f903 100644 --- a/core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/impl/MovieInteractorImpl.kt +++ b/core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/impl/MovieInteractorImpl.kt @@ -12,17 +12,20 @@ import org.michaelbel.movies.common.dispatchers.MoviesDispatchers import org.michaelbel.movies.common.list.MovieList import org.michaelbel.movies.interactor.MovieInteractor import org.michaelbel.movies.interactor.ktx.nameOrLocalList -import org.michaelbel.movies.interactor.remote.MoviesRemoteMediator +import org.michaelbel.movies.interactor.remote.FeedMoviesRemoteMediator +import org.michaelbel.movies.interactor.remote.SearchMoviesRemoteMediator import org.michaelbel.movies.network.Either import org.michaelbel.movies.network.model.MovieResponse import org.michaelbel.movies.persistence.database.AppDatabase import org.michaelbel.movies.persistence.database.entity.MovieDb import org.michaelbel.movies.repository.MovieRepository import org.michaelbel.movies.repository.PagingKeyRepository +import org.michaelbel.movies.repository.SearchRepository @Singleton internal class MovieInteractorImpl @Inject constructor( private val dispatchers: MoviesDispatchers, + private val searchRepository: SearchRepository, private val movieRepository: MovieRepository, private val pagingKeyRepository: PagingKeyRepository, private val database: AppDatabase, @@ -33,7 +36,7 @@ internal class MovieInteractorImpl @Inject constructor( config = PagingConfig( pageSize = MovieResponse.DEFAULT_PAGE_SIZE ), - remoteMediator = MoviesRemoteMediator( + remoteMediator = FeedMoviesRemoteMediator( movieRepository = movieRepository, pagingKeyRepository = pagingKeyRepository, database = database, @@ -43,6 +46,22 @@ internal class MovieInteractorImpl @Inject constructor( ).flow } + override fun moviesPagingData(searchQuery: String): Flow> { + return Pager( + config = PagingConfig( + pageSize = MovieResponse.DEFAULT_PAGE_SIZE + ), + remoteMediator = SearchMoviesRemoteMediator( + pagingKeyRepository = pagingKeyRepository, + searchRepository = searchRepository, + movieRepository = movieRepository, + database = database, + query = searchQuery + ), + pagingSourceFactory = { movieRepository.moviesPagingSource(searchQuery) } + ).flow + } + override fun moviesPagingSource(pagingKey: String): PagingSource { return movieRepository.moviesPagingSource(pagingKey) } @@ -78,12 +97,6 @@ internal class MovieInteractorImpl @Inject constructor( } } - override suspend fun insertMovies(pagingKey: String, movies: List) { - return withContext(dispatchers.io) { - movieRepository.insertMovies(pagingKey, movies) - } - } - override suspend fun insertMovie(pagingKey: String, movie: MovieDb) { return withContext(dispatchers.io) { movieRepository.insertMovie(pagingKey, movie) diff --git a/core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/impl/PagingKeyInteractorImpl.kt b/core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/impl/PagingKeyInteractorImpl.kt deleted file mode 100644 index 6ddd1261d..000000000 --- a/core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/impl/PagingKeyInteractorImpl.kt +++ /dev/null @@ -1,33 +0,0 @@ -package org.michaelbel.movies.interactor.impl - -import javax.inject.Inject -import javax.inject.Singleton -import kotlinx.coroutines.withContext -import org.michaelbel.movies.common.dispatchers.MoviesDispatchers -import org.michaelbel.movies.interactor.PagingKeyInteractor -import org.michaelbel.movies.repository.PagingKeyRepository - -@Singleton -internal class PagingKeyInteractorImpl @Inject constructor( - private val dispatchers: MoviesDispatchers, - private val pagingKeyRepository: PagingKeyRepository -): PagingKeyInteractor { - - override suspend fun page(pagingKey: String): Int? { - return withContext(dispatchers.io) { - pagingKeyRepository.page(pagingKey) - } - } - - override suspend fun removePagingKey(pagingKey: String) { - return withContext(dispatchers.io) { - pagingKeyRepository.removePagingKey(pagingKey) - } - } - - override suspend fun insertPagingKey(pagingKey: String, page: Int) { - return withContext(dispatchers.io) { - pagingKeyRepository.insertPagingKey(pagingKey, page) - } - } -} \ No newline at end of file diff --git a/core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/impl/SearchInteractorImpl.kt b/core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/impl/SearchInteractorImpl.kt deleted file mode 100644 index d8c06977c..000000000 --- a/core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/impl/SearchInteractorImpl.kt +++ /dev/null @@ -1,23 +0,0 @@ -package org.michaelbel.movies.interactor.impl - -import javax.inject.Inject -import javax.inject.Singleton -import kotlinx.coroutines.withContext -import org.michaelbel.movies.common.dispatchers.MoviesDispatchers -import org.michaelbel.movies.interactor.SearchInteractor -import org.michaelbel.movies.network.model.MovieResponse -import org.michaelbel.movies.network.model.Result -import org.michaelbel.movies.repository.SearchRepository - -@Singleton -internal class SearchInteractorImpl @Inject constructor( - private val dispatchers: MoviesDispatchers, - private val searchRepository: SearchRepository -): SearchInteractor { - - override suspend fun searchMoviesResult(query: String, page: Int): Result { - return withContext(dispatchers.io) { - searchRepository.searchMoviesResult(query, page) - } - } -} \ No newline at end of file diff --git a/core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/remote/MoviesRemoteMediator.kt b/core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/remote/FeedMoviesRemoteMediator.kt similarity index 98% rename from core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/remote/MoviesRemoteMediator.kt rename to core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/remote/FeedMoviesRemoteMediator.kt index b5420c0a0..d6f2ad401 100644 --- a/core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/remote/MoviesRemoteMediator.kt +++ b/core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/remote/FeedMoviesRemoteMediator.kt @@ -15,7 +15,7 @@ import org.michaelbel.movies.persistence.database.entity.MovieDb import org.michaelbel.movies.repository.MovieRepository import org.michaelbel.movies.repository.PagingKeyRepository -class MoviesRemoteMediator( +class FeedMoviesRemoteMediator( private val pagingKeyRepository: PagingKeyRepository, private val movieRepository: MovieRepository, private val database: AppDatabase, diff --git a/feature/search-impl/src/main/kotlin/org/michaelbel/movies/search/remote/SearchMoviesRemoteMediator.kt b/core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/remote/SearchMoviesRemoteMediator.kt similarity index 60% rename from feature/search-impl/src/main/kotlin/org/michaelbel/movies/search/remote/SearchMoviesRemoteMediator.kt rename to core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/remote/SearchMoviesRemoteMediator.kt index c89aae82c..5be183603 100644 --- a/feature/search-impl/src/main/kotlin/org/michaelbel/movies/search/remote/SearchMoviesRemoteMediator.kt +++ b/core/interactor/src/main/kotlin/org/michaelbel/movies/interactor/remote/SearchMoviesRemoteMediator.kt @@ -1,19 +1,26 @@ -package org.michaelbel.movies.search.remote +package org.michaelbel.movies.interactor.remote import androidx.paging.LoadType import androidx.paging.PagingState import androidx.paging.RemoteMediator +import androidx.room.withTransaction import org.michaelbel.movies.common.exceptions.PageEmptyException -import org.michaelbel.movies.interactor.Interactor import org.michaelbel.movies.network.ktx.isEmpty import org.michaelbel.movies.network.ktx.isPaginationReached import org.michaelbel.movies.network.ktx.nextPage import org.michaelbel.movies.network.model.MovieResponse import org.michaelbel.movies.network.model.Result +import org.michaelbel.movies.persistence.database.AppDatabase import org.michaelbel.movies.persistence.database.entity.MovieDb +import org.michaelbel.movies.repository.MovieRepository +import org.michaelbel.movies.repository.PagingKeyRepository +import org.michaelbel.movies.repository.SearchRepository class SearchMoviesRemoteMediator( - private val interactor: Interactor, + private val pagingKeyRepository: PagingKeyRepository, + private val movieRepository: MovieRepository, + private val searchRepository: SearchRepository, + private val database: AppDatabase, private val query: String ): RemoteMediator() { @@ -31,7 +38,7 @@ class SearchMoviesRemoteMediator( return reachedResult } LoadType.APPEND -> { - interactor.page(query) ?: return reachedResult + pagingKeyRepository.page(query) ?: return reachedResult } } @@ -39,7 +46,7 @@ class SearchMoviesRemoteMediator( throw PageEmptyException } - val moviesResult: Result = interactor.searchMoviesResult( + val moviesResult: Result = searchRepository.searchMoviesResult( query = query, page = loadKey ?: 1 ) @@ -48,15 +55,13 @@ class SearchMoviesRemoteMediator( throw PageEmptyException } - if (loadType == LoadType.REFRESH) { - interactor.run { - removePagingKey(query) - removeMovies(query) + database.withTransaction { + if (loadType == LoadType.REFRESH) { + pagingKeyRepository.removePagingKey(query) + movieRepository.removeMovies(query) } - } - interactor.run { - insertPagingKey(query, moviesResult.nextPage) - insertMovies(query, moviesResult.results) + pagingKeyRepository.insertPagingKey(query, moviesResult.nextPage) + movieRepository.insertMovies(query, moviesResult.results) } MediatorResult.Success(endOfPaginationReached = moviesResult.isPaginationReached) diff --git a/feature/search-impl/build.gradle.kts b/feature/search-impl/build.gradle.kts index be20c9ad6..1c8baae08 100644 --- a/feature/search-impl/build.gradle.kts +++ b/feature/search-impl/build.gradle.kts @@ -25,8 +25,7 @@ android { kotlinOptions { freeCompilerArgs = freeCompilerArgs + listOf( "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", - "-opt-in=androidx.compose.material3.ExperimentalMaterial3Api", - "-opt-in=androidx.paging.ExperimentalPagingApi" + "-opt-in=androidx.compose.material3.ExperimentalMaterial3Api" ) } @@ -59,7 +58,6 @@ dependencies { implementation(project(":core:interactor")) implementation(project(":core:network")) implementation(project(":core:notifications")) - implementation(project(":core:persistence")) testImplementation(libs.junit) androidTestImplementation(libs.androidx.test.ext.junit.ktx) diff --git a/feature/search-impl/src/main/kotlin/org/michaelbel/movies/search/SearchViewModel.kt b/feature/search-impl/src/main/kotlin/org/michaelbel/movies/search/SearchViewModel.kt index c43a44b53..75f75e0ee 100644 --- a/feature/search-impl/src/main/kotlin/org/michaelbel/movies/search/SearchViewModel.kt +++ b/feature/search-impl/src/main/kotlin/org/michaelbel/movies/search/SearchViewModel.kt @@ -1,7 +1,5 @@ package org.michaelbel.movies.search -import androidx.paging.Pager -import androidx.paging.PagingConfig import androidx.paging.PagingData import androidx.paging.cachedIn import dagger.hilt.android.lifecycle.HiltViewModel @@ -21,10 +19,8 @@ import org.michaelbel.movies.common.viewmodel.BaseViewModel import org.michaelbel.movies.interactor.Interactor import org.michaelbel.movies.network.connectivity.NetworkManager import org.michaelbel.movies.network.connectivity.NetworkStatus -import org.michaelbel.movies.network.model.MovieResponse import org.michaelbel.movies.persistence.database.entity.MovieDb import org.michaelbel.movies.persistence.database.entity.SuggestionDb -import org.michaelbel.movies.search.remote.SearchMoviesRemoteMediator @HiltViewModel class SearchViewModel @Inject constructor( @@ -66,24 +62,9 @@ class SearchViewModel @Inject constructor( private val _active: MutableStateFlow = MutableStateFlow(true) val active: StateFlow = _active.asStateFlow() - val pagingItems: Flow> = query.flatMapLatest { query -> - Pager( - config = PagingConfig( - pageSize = MovieResponse.DEFAULT_PAGE_SIZE - ), - remoteMediator = SearchMoviesRemoteMediator( - interactor = interactor, - query = query - ), - pagingSourceFactory = { interactor.moviesPagingSource(query) } - ).flow - .stateIn( - scope = this, - started = SharingStarted.Lazily, - initialValue = PagingData.empty() - ) - .cachedIn(this) - } + val pagingDataFlow: Flow> = query + .flatMapLatest { query -> interactor.moviesPagingData(query) } + .cachedIn(this) init { loadSuggestions() diff --git a/feature/search-impl/src/main/kotlin/org/michaelbel/movies/search/ui/SearchScreenContent.kt b/feature/search-impl/src/main/kotlin/org/michaelbel/movies/search/ui/SearchScreenContent.kt index fd6141576..19157044d 100644 --- a/feature/search-impl/src/main/kotlin/org/michaelbel/movies/search/ui/SearchScreenContent.kt +++ b/feature/search-impl/src/main/kotlin/org/michaelbel/movies/search/ui/SearchScreenContent.kt @@ -57,7 +57,7 @@ fun SearchRoute( modifier: Modifier = Modifier, viewModel: SearchViewModel = hiltViewModel() ) { - val pagingItems: LazyPagingItems = viewModel.pagingItems.collectAsLazyPagingItems() + val pagingItems: LazyPagingItems = viewModel.pagingDataFlow.collectAsLazyPagingItems() val currentFeedView: FeedView by viewModel.currentFeedView.collectAsStateWithLifecycle() val networkStatus: NetworkStatus by viewModel.networkStatus.collectAsStateWithLifecycle() val suggestions: List by viewModel.suggestionsFlow.collectAsStateWithLifecycle()