Firestore pagination with Jetpack Paging 3 - startBefore not loading the correct page

461 views Asked by At

This is my attempt at paginating a collection of Firestore chat messages with Paging 3. It correctly loads the next pages, so the startAfter operator seems to be working as expected. But it's not loading previous pages correctly. Instead, it always loads the very first page again and appends it at the beginning of the list. (I start dropping pages after 100 items so lazy loading works in both directions).

The prevKey seems to be passed correctly. It has the correct value at beginning of the load method right before we build the query.

timeStamp is a Firestore server timestamp annotated with @ServerTimestamp if that matters.

class ChatMessagesPagingSource(
    private val messageCollection: CollectionReference
) : PagingSource<ChatMessagesPagingSource.PagingKey, ChatMessage>() {

    override suspend fun load(params: LoadParams<PagingKey>): LoadResult<PagingKey, ChatMessage> {
        return try {

            var query = messageCollection
                .orderBy("timeStamp", Query.Direction.DESCENDING)
                .limit(params.loadSize.toLong())

            val key = params.key

            Timber.d("key = $key")

            query = when (key) {
                is PagingKey.PreviousKey -> query.endBefore(key.endBefore)
                is PagingKey.NextKey -> query.startAfter(key.startAfter)
                null -> query
            }

            val querySnapshot = query.get().await()
            val chatMessages = querySnapshot.toObjects(ChatMessage::class.java)
            val firstDoc = querySnapshot.documents.firstOrNull()
            val lastDoc = querySnapshot.documents.lastOrNull()
            val prevKey = if (firstDoc != null) PagingKey.PreviousKey(firstDoc) else null
            val nextKey = if (lastDoc != null) PagingKey.NextKey(lastDoc) else null

            Timber.d("first message: ${chatMessages.firstOrNull()}")
            Timber.d("last message: ${chatMessages.lastOrNull()}")

            LoadResult.Page(
                data = chatMessages,
                prevKey = prevKey,
                nextKey = nextKey
            )
        } catch (e: Exception) {
            LoadResult.Error(e)
        }
    }

    sealed class PagingKey{
        data class PreviousKey(val endBefore: DocumentSnapshot) : PagingKey()
        data class NextKey(val startAfter: DocumentSnapshot) : PagingKey()
    }
}
1

There are 1 answers

0
dlam On

You need to check for the type of LoadParams, to see if it is refresh, prepend or append.

Since your query always fetches items after in descending order, when it requests a prepend you're probably loading the incorrect items.