Fix Timeline not loading (#2398)
* fix cached timeline * fix network timeline * delete unused inc / dec extensions * fix tests and bug in network timeline * add db migration * remove unused import * commit 31.json * improve placeholder inserting logic, add comment * fix tests * improve tests
This commit is contained in:
parent
c47804997c
commit
f2529a8e61
12 changed files with 938 additions and 120 deletions
|
@ -30,7 +30,6 @@ import com.keylesspalace.tusky.db.TimelineStatusEntity
|
|||
import com.keylesspalace.tusky.db.TimelineStatusWithAccount
|
||||
import com.keylesspalace.tusky.entity.Status
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import com.keylesspalace.tusky.util.dec
|
||||
import kotlinx.coroutines.rx3.await
|
||||
import retrofit2.HttpException
|
||||
|
||||
|
@ -102,9 +101,14 @@ class CachedTimelineRemoteMediator(
|
|||
db.withTransaction {
|
||||
val overlappedStatuses = replaceStatusRange(statuses, state)
|
||||
|
||||
if (loadType == LoadType.REFRESH && overlappedStatuses == 0 && statuses.isNotEmpty() && !dbEmpty) {
|
||||
/* In case we loaded a whole page and there was no overlap with existing statuses,
|
||||
we insert a placeholder because there might be even more unknown statuses */
|
||||
if (loadType == LoadType.REFRESH && overlappedStatuses == 0 && statuses.size == state.config.pageSize && !dbEmpty) {
|
||||
/* This overrides the last of the newly loaded statuses with a placeholder
|
||||
to guarantee the placeholder has an id that exists on the server as not all
|
||||
servers handle client generated ids as expected */
|
||||
timelineDao.insertStatus(
|
||||
Placeholder(statuses.last().id.dec(), loading = false).toEntity(activeAccount.id)
|
||||
Placeholder(statuses.last().id, loading = false).toEntity(activeAccount.id)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,8 +41,6 @@ import com.keylesspalace.tusky.entity.Poll
|
|||
import com.keylesspalace.tusky.network.FilterModel
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import com.keylesspalace.tusky.network.TimelineCases
|
||||
import com.keylesspalace.tusky.util.dec
|
||||
import com.keylesspalace.tusky.util.inc
|
||||
import com.keylesspalace.tusky.viewdata.StatusViewData
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
@ -149,9 +147,11 @@ class CachedTimelineViewModel @Inject constructor(
|
|||
|
||||
timelineDao.insertStatus(Placeholder(placeholderId, loading = true).toEntity(activeAccount.id))
|
||||
|
||||
val nextPlaceholderId = timelineDao.getNextPlaceholderIdAfter(activeAccount.id, placeholderId)
|
||||
|
||||
val response = api.homeTimeline(maxId = placeholderId.inc(), sinceId = nextPlaceholderId, limit = LOAD_AT_ONCE).await()
|
||||
val response = db.withTransaction {
|
||||
val idAbovePlaceholder = timelineDao.getIdAbove(activeAccount.id, placeholderId)
|
||||
val nextPlaceholderId = timelineDao.getNextPlaceholderIdAfter(activeAccount.id, placeholderId)
|
||||
api.homeTimeline(maxId = idAbovePlaceholder, sinceId = nextPlaceholderId, limit = LOAD_AT_ONCE)
|
||||
}.await()
|
||||
|
||||
val statuses = response.body()
|
||||
if (!response.isSuccessful || statuses == null) {
|
||||
|
@ -185,9 +185,14 @@ class CachedTimelineViewModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
if (overlappedStatuses == 0 && statuses.isNotEmpty()) {
|
||||
/* In case we loaded a whole page and there was no overlap with existing statuses,
|
||||
we insert a placeholder because there might be even more unknown statuses */
|
||||
if (overlappedStatuses == 0 && statuses.size == LOAD_AT_ONCE) {
|
||||
/* This overrides the last of the newly loaded statuses with a placeholder
|
||||
to guarantee the placeholder has an id that exists on the server as not all
|
||||
servers handle client generated ids as expected */
|
||||
timelineDao.insertStatus(
|
||||
Placeholder(statuses.last().id.dec(), loading = false).toEntity(activeAccount.id)
|
||||
Placeholder(statuses.last().id, loading = false).toEntity(activeAccount.id)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ import androidx.paging.RemoteMediator
|
|||
import com.keylesspalace.tusky.components.timeline.util.ifExpected
|
||||
import com.keylesspalace.tusky.db.AccountManager
|
||||
import com.keylesspalace.tusky.util.HttpHeaderLink
|
||||
import com.keylesspalace.tusky.util.dec
|
||||
import com.keylesspalace.tusky.util.toViewData
|
||||
import com.keylesspalace.tusky.viewdata.StatusViewData
|
||||
import retrofit2.HttpException
|
||||
|
@ -93,7 +92,7 @@ class NetworkTimelineRemoteMediator(
|
|||
viewModel.statusData.addAll(0, data)
|
||||
|
||||
if (insertPlaceholder) {
|
||||
viewModel.statusData.add(statuses.size, StatusViewData.Placeholder(statuses.last().id.dec(), false))
|
||||
viewModel.statusData[statuses.size - 1] = StatusViewData.Placeholder(statuses.last().id, false)
|
||||
}
|
||||
} else {
|
||||
val linkHeader = statusResponse.headers()["Link"]
|
||||
|
|
|
@ -35,9 +35,7 @@ import com.keylesspalace.tusky.entity.Status
|
|||
import com.keylesspalace.tusky.network.FilterModel
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import com.keylesspalace.tusky.network.TimelineCases
|
||||
import com.keylesspalace.tusky.util.dec
|
||||
import com.keylesspalace.tusky.util.getDomain
|
||||
import com.keylesspalace.tusky.util.inc
|
||||
import com.keylesspalace.tusky.util.isLessThan
|
||||
import com.keylesspalace.tusky.util.isLessThanOrEqual
|
||||
import com.keylesspalace.tusky.util.toViewData
|
||||
|
@ -142,8 +140,10 @@ class NetworkTimelineViewModel @Inject constructor(
|
|||
statusData.indexOfFirst { it is StatusViewData.Placeholder && it.id == placeholderId }
|
||||
statusData[placeholderIndex] = StatusViewData.Placeholder(placeholderId, isLoading = true)
|
||||
|
||||
val idAbovePlaceholder = statusData.getOrNull(placeholderIndex - 1)?.id
|
||||
|
||||
val statusResponse = fetchStatusesForKind(
|
||||
fromId = placeholderId.inc(),
|
||||
fromId = idAbovePlaceholder,
|
||||
uptoId = null,
|
||||
limit = 20
|
||||
)
|
||||
|
@ -157,7 +157,7 @@ class NetworkTimelineViewModel @Inject constructor(
|
|||
statusData.removeAt(placeholderIndex)
|
||||
|
||||
val activeAccount = accountManager.activeAccount!!
|
||||
val data = statuses.map { status ->
|
||||
val data: MutableList<StatusViewData> = statuses.map { status ->
|
||||
status.toViewData(
|
||||
isShowingContent = activeAccount.alwaysShowSensitiveMedia || !status.actionableStatus.sensitive,
|
||||
isExpanded = activeAccount.alwaysOpenSpoiler,
|
||||
|
@ -175,7 +175,7 @@ class NetworkTimelineViewModel @Inject constructor(
|
|||
data.mapIndexed { i, status -> i to statusData.firstOrNull { it.asStatusOrNull()?.id == status.id }?.asStatusOrNull() }
|
||||
.filter { (_, oldStatus) -> oldStatus != null }
|
||||
.forEach { (i, oldStatus) ->
|
||||
data[i] = data[i]
|
||||
data[i] = data[i].asStatusOrNull()!!
|
||||
.copy(
|
||||
isShowingContent = oldStatus!!.isShowingContent,
|
||||
isExpanded = oldStatus.isExpanded,
|
||||
|
@ -190,7 +190,7 @@ class NetworkTimelineViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
} else {
|
||||
statusData.add(overlappedFrom, StatusViewData.Placeholder(statuses.last().id.dec(), isLoading = false))
|
||||
data[data.size - 1] = StatusViewData.Placeholder(statuses.last().id, isLoading = false)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -240,7 +240,7 @@ class NetworkTimelineViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun fullReload() {
|
||||
nextKey = statusData.firstOrNull { it is StatusViewData.Concrete }?.asStatusOrNull()?.id?.inc()
|
||||
nextKey = statusData.firstOrNull { it is StatusViewData.Concrete }?.asStatusOrNull()?.id
|
||||
statusData.clear()
|
||||
currentSource?.invalidate()
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue