android / codelab-android-paging

Jetpack Paging codelab
Apache License 2.0
495 stars 268 forks source link

Cannot load on next page using api-room (mediator) #119

Closed johnjake closed 3 years ago

johnjake commented 4 years ago

I try using it in the movie api, and works fine without room, but when I loaded the result first to room or using Mediator I am only getting one page and cannot load the next page. If someone having the same issue and solve it, I really appreciate if you could pinpoint or share the solution. Thanks and best regards.

private const val STARTING_PAGE_INDEX = 1

@OptIn(ExperimentalPagingApi::class)
class SearchPagingMediator(
    private val query: String,
    private val apiServices: ApiServices,
    private val appDatabase: AppDatabase
) : RemoteMediator<Int, DBMovieDiscover>() {

    private val mapper by lazy { GlobalContext.get().koin.get<DiscoverMapper>() }

    override suspend fun load(
        loadType: LoadType,
        state: PagingState<Int, DBMovieDiscover>
    ): MediatorResult {
        val page = when (loadType) {
            LoadType.REFRESH -> {
                val remoteKeys = getRemoteKeyClosestToCurrentPosition(state)
                remoteKeys?.nextKey?.minus(1) ?: STARTING_PAGE_INDEX
            }
            LoadType.PREPEND -> {
                val remoteKeys = getRemoteKeyForFirstItem(state)
                // If the previous key is null, then we can't request more data
                remoteKeys?.prevKey ?: return MediatorResult.Success(endOfPaginationReached = true)
            }
            LoadType.APPEND -> {
                val remoteKeys = getRemoteKeyForLastItem(state)
                remoteKeys?.nextKey ?: return MediatorResult.Success(endOfPaginationReached = true)
            }
        }

        try {
            val apiKey: String = BuildConfig.API_KEY
            val response = page.let { apiServices.getSearchMovieView(apiKey, query, it as Int) }
            val isEndOfList = response.results.isEmpty()
            appDatabase.withTransaction {
                // clear all tables in the database
                if (loadType == LoadType.REFRESH) {
                    appDatabase.remoteKeysDao().clearRemoteKeys()
                    appDatabase.nowPlayingMovieDao().clearAllNowPlayingMovie()
                }
                val prevKey = if (page == STARTING_PAGE_INDEX) null else page - 1
                val nextKey = if (isEndOfList) null else page + 1
                val keys = response.results.map {
                    DBRemoteKeys(repoId = it.id, prevKey = prevKey, nextKey = nextKey)
                }
                appDatabase.remoteKeysDao().insertAll(keys)
                val apiList = response.results
                apiList.forEach {
                    val dbMovie = mapper.mapFromData(it)
                    appDatabase.discoverDao().insertMovieScreen(dbMovie)
                }
            }
            return MediatorResult.Success(endOfPaginationReached = !response.results.isEmpty()!!)
        } catch (exception: IOException) {
            return MediatorResult.Error(exception)
        } catch (exception: HttpException) {
            return MediatorResult.Error(exception)
        }
    }

    private suspend fun getRemoteKeyForLastItem(state: PagingState<Int, DBMovieDiscover>): DBRemoteKeys? {
        return state.pages.lastOrNull() { it.data.isNotEmpty() }?.data?.lastOrNull()
            ?.let { movie ->
                appDatabase.remoteKeysDao().remoteKeysMovieId(movie.id)
            }
    }

    private suspend fun getRemoteKeyForFirstItem(state: PagingState<Int, DBMovieDiscover>): DBRemoteKeys? {
        return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
            ?.let { repo ->
                appDatabase.remoteKeysDao().remoteKeysMovieId(repo.id)
            }
    }

    private suspend fun getRemoteKeyClosestToCurrentPosition(
        state: PagingState<Int, DBMovieDiscover>
    ): DBRemoteKeys? {
        return state.anchorPosition?.let { position ->
            state.closestItemToPosition(position)?.id?.let { repoId ->
                appDatabase.remoteKeysDao().remoteKeysMovieId(repoId)
            }
        }
    }
}
florina-muntenescu commented 3 years ago

Not an issue with the codelab.

mahtab379 commented 2 years ago

return MediatorResult.Success(endOfPaginationReached = !response.results.isEmpty()!!) should be return MediatorResult.Success(endOfPaginationReached = response.results.isNullOrEmpty())