Implement progressive fetching for messages

Start fetching from API when messages fetched in last API request are shown in the list

MAILAND-2089
This commit is contained in:
Davide Farella 2021-08-19 17:40:24 +02:00 committed by Davide Giuseppe Farella
parent 62ba8725fc
commit 968e2e2956
4 changed files with 37 additions and 27 deletions

View File

@ -211,13 +211,15 @@ class MailboxRecyclerViewAdapter(
mailboxLocation = locationType
}
fun getOldestMailboxItemTimestamp(): Long {
val lastItemTimeMs = if (mailboxItems.isNotEmpty()) {
mailboxItems.minOf { it.lastMessageTimeMs }
} else {
System.currentTimeMillis()
}
return lastItemTimeMs / 1000
}
/**
* @return `true` if any items withing the given positions' range has an [MailboxUiItem.itemId] that matches one
* from [mailboxItemsIds]
*/
fun isAnyMailboxItemWithinPositions(
mailboxItemsIds: List<String>,
startPosition: Int,
endPosition: Int
) = mailboxItems.subList(startPosition, endPosition + 1)
.any { it.itemId in mailboxItemsIds }
}

View File

@ -30,6 +30,7 @@ import me.proton.core.domain.arch.ResponseSource
import me.proton.core.domain.arch.mapSuccess
import me.proton.core.domain.arch.onSuccess
import me.proton.core.util.kotlin.invoke
import timber.log.Timber
/**
* [ProtonStore] works in a similar manner as [com.dropbox.android.external.store4.Store] but provide us a more
@ -102,6 +103,7 @@ class ProtonStore<Key : Any, ApiModel : Any, DatabaseModel : Any, DomainModel :
freshAsApiModel(key).toDomainModelsDataResult()
private suspend fun freshAsApiModel(key: Key): DataResult<ApiModel> {
Timber.i("Fetching for: $key")
@Suppress("TooGenericExceptionCaught")
val apiResult = try {
val apiModels = fetcher(key)

View File

@ -210,6 +210,7 @@ internal class MailboxActivity :
setNewLabel(value ?: EMPTY_STRING)
}
private var mailboxLabelName: String? = null
private var lastFetchedMailboxItemsIds = emptyList<String>()
private var refreshMailboxJobRunning = false
private lateinit var syncUUID: String
private var customizeSwipeSnackShown = false
@ -219,7 +220,6 @@ internal class MailboxActivity :
override val currentLabelId get() = mailboxLabelId
override fun getLayoutId(): Int = R.layout.activity_mailbox
override fun onCreate(savedInstanceState: Bundle?) {
@ -571,6 +571,7 @@ internal class MailboxActivity :
if (isLoadingMore.get()) setLoadingMore(false)
}
is MailboxState.ApiRefresh -> {
lastFetchedMailboxItemsIds = state.lastFetchedMessagesIds
setRefreshing(false)
}
is MailboxState.Data -> {
@ -611,19 +612,29 @@ internal class MailboxActivity :
if (!scrollStateChanged) {
return
}
val layoutManager = recyclerView.layoutManager as LinearLayoutManager?
val adapter = recyclerView.adapter
val lastVisibleItem = layoutManager!!.findLastVisibleItemPosition()
val lastPosition = adapter!!.itemCount - 1
if (lastVisibleItem == lastPosition && dy > 0 && !setLoadingMore(true)) {
mailboxViewModel.loadMore()
// TODO: remove, use loadMore only
loadMailboxItems(oldestItemTimestamp = mailboxAdapter.getOldestMailboxItemTimestamp())
val layoutManager = recyclerView.layoutManager as LinearLayoutManager
val firstCompletelyVisibleItemPosition = layoutManager.findFirstCompletelyVisibleItemPosition()
// Load more when showing last fetched messages or at the end of the list
if (dy > 0 && isLoadingMore.get().not()) {
val lastCompletelyVisibleItemPosition = layoutManager.findLastCompletelyVisibleItemPosition()
val isAtBottomOfTheList = lastCompletelyVisibleItemPosition == mailboxAdapter.itemCount - 1
fun inAnyLastFetchedMailboxItemVisible() =
mailboxAdapter.isAnyMailboxItemWithinPositions(
mailboxItemsIds = lastFetchedMailboxItemsIds,
startPosition = firstCompletelyVisibleItemPosition,
endPosition = lastCompletelyVisibleItemPosition
)
if (isAtBottomOfTheList || inAnyLastFetchedMailboxItemVisible()) {
setLoadingMore(true)
mailboxViewModel.loadMore()
lastFetchedMailboxItemsIds = emptyList()
}
}
// Increase the elevation if the list is scrolled down and decrease if it is scrolled to the top
val firstVisibleItem = layoutManager.findFirstCompletelyVisibleItemPosition()
if (firstVisibleItem == 0) {
if (firstCompletelyVisibleItemPosition == 0) {
setElevationOnToolbarAndStatusView(false)
} else {
setElevationOnToolbarAndStatusView(true)
@ -631,13 +642,7 @@ internal class MailboxActivity :
}
}
private fun loadMailboxItems(
includeLabels: Boolean = false,
refreshMessages: Boolean = false,
oldestItemTimestamp: Long = now()
) {
Timber.v("loadMailboxItems last: $oldestItemTimestamp")
private fun loadMailboxItems() {
mailboxViewModel.loadMailboxItems()
}
@ -667,7 +672,7 @@ internal class MailboxActivity :
// TODO: is this needed?
mailboxViewModel.loadMore()
// TODO: remove, use loadMore only
loadMailboxItems(includeLabels = true)
loadMailboxItems()
}
mailboxViewModel.checkConnectivityDelayed()
}

View File

@ -367,6 +367,7 @@ class MailboxViewModel @Inject constructor(
}
is GetMessagesResult.ApiRefresh -> {
if (hasReceivedFirstApiRefresh == null) hasReceivedFirstApiRefresh = true
else if (hasReceivedFirstApiRefresh == true) hasReceivedFirstApiRefresh = false
MailboxState.ApiRefresh(
lastFetchedMessagesIds = result.lastFetchedMessages.mapNotNull { it.messageId }