Refactor threads to support the new timeline implementation

This commit is contained in:
ariskotsomitopoulos 2022-01-11 12:13:53 +02:00
parent 1b41a72e72
commit 37ec3fdf84
2 changed files with 73 additions and 28 deletions

View file

@ -93,7 +93,7 @@ internal class TimelineChunk(private val chunkEntity: ChunkEntity,
handleDatabaseChangeSet(frozenResults, changeSet)
}
private var timelineEventEntities: RealmResults<TimelineEventEntity> = chunkEntity.sortedTimelineEvents()
private var timelineEventEntities: RealmResults<TimelineEventEntity> = chunkEntity.sortedTimelineEvents(timelineSettings.rootThreadEventId)
private val builtEvents: MutableList<TimelineEvent> = Collections.synchronizedList(ArrayList())
private val builtEventsIndexes: MutableMap<String, Int> = Collections.synchronizedMap(HashMap<String, Int>())
@ -138,13 +138,18 @@ internal class TimelineChunk(private val chunkEntity: ChunkEntity,
} else if (direction == Timeline.Direction.BACKWARDS && prevChunk != null) {
return prevChunk?.loadMore(count, direction, fetchOnServerIfNeeded) ?: LoadMoreResult.FAILURE
}
val loadFromStorageCount = loadFromStorage(count, direction)
Timber.v("Has loaded $loadFromStorageCount items from storage in $direction")
val offsetCount = count - loadFromStorageCount
val loadFromStorage = loadFromStorage(count, direction).also{
logLoadedFromStorage(it,direction)
}
val offsetCount = count - loadFromStorage.numberOfEvents
return if (direction == Timeline.Direction.FORWARDS && isLastForward.get()) {
LoadMoreResult.REACHED_END
} else if (direction == Timeline.Direction.BACKWARDS && isLastBackward.get()) {
LoadMoreResult.REACHED_END
} else if (timelineSettings.isThreadTimeline() && loadFromStorage.threadReachedEnd) {
LoadMoreResult.REACHED_END
} else if (offsetCount == 0) {
LoadMoreResult.SUCCESS
} else {
@ -188,6 +193,15 @@ internal class TimelineChunk(private val chunkEntity: ChunkEntity,
}
}
/**
* Simple log that displays the number and timeline of loaded events
*/
private fun logLoadedFromStorage(loadedFromStorage: LoadedFromStorage, direction: Timeline.Direction) =
Timber.v("[" +
"${if (timelineSettings.isThreadTimeline()) "ThreadTimeLine" else "Timeline"}] Has loaded " +
"${loadedFromStorage.numberOfEvents} items from storage in $direction " +
if (timelineSettings.isThreadTimeline() && loadedFromStorage.threadReachedEnd) "[Reached End]" else "")
fun getBuiltEventIndex(eventId: String, searchInNext: Boolean, searchInPrev: Boolean): Int? {
val builtEventIndex = builtEventsIndexes[eventId]
if (builtEventIndex != null) {
@ -268,29 +282,31 @@ internal class TimelineChunk(private val chunkEntity: ChunkEntity,
/**
* This method tries to read events from the current chunk.
* @return the number of events loaded. If we are in a thread timeline it also returns
* whether or not we reached the end/root message
*/
private suspend fun loadFromStorage(count: Int, direction: Timeline.Direction): Int {
val displayIndex = getNextDisplayIndex(direction) ?: return 0
private suspend fun loadFromStorage(count: Int, direction: Timeline.Direction): LoadedFromStorage {
val displayIndex = getNextDisplayIndex(direction) ?: return LoadedFromStorage()
val baseQuery = timelineEventEntities.where()
val timelineEvents = if (timelineSettings.rootThreadEventId != null) {
baseQuery
.beginGroup()
.equalTo(TimelineEventEntityFields.ROOT.ROOT_THREAD_EVENT_ID, timelineSettings.rootThreadEventId)
.or()
.equalTo(TimelineEventEntityFields.ROOT.EVENT_ID, timelineSettings.rootThreadEventId)
.endGroup()
.offsets(direction, count, displayIndex)
.findAll()
.orEmpty()
} else {
baseQuery
.offsets(direction, count, displayIndex)
.findAll()
.orEmpty()
}
// val timelineEvents = if (timelineSettings.rootThreadEventId != null) {
// baseQuery
// .beginGroup()
// .equalTo(TimelineEventEntityFields.ROOT.ROOT_THREAD_EVENT_ID, timelineSettings.rootThreadEventId)
// .or()
// .equalTo(TimelineEventEntityFields.ROOT.EVENT_ID, timelineSettings.rootThreadEventId)
// .endGroup()
// .offsets(direction, count, displayIndex)
// .findAll()
// .orEmpty()
// } else {
val timelineEvents = baseQuery
.offsets(direction, count, displayIndex)
.findAll()
.orEmpty()
// }
if (timelineEvents.isEmpty()) return 0
if (timelineEvents.isEmpty()) return LoadedFromStorage()
fetchRootThreadEventsIfNeeded(timelineEvents)
if (direction == Timeline.Direction.FORWARDS) {
builtEventsIndexes.entries.forEach { it.setValue(it.value + timelineEvents.size) }
@ -309,9 +325,20 @@ internal class TimelineChunk(private val chunkEntity: ChunkEntity,
builtEvents.add(timelineEvent)
}
}
return timelineEvents.size
return LoadedFromStorage(
threadReachedEnd = threadReachedEnd(timelineEvents),
numberOfEvents = timelineEvents.size)
}
/**
* Returns whether or not the the thread has reached end. It returned false if the current timeline
* is not a thread timeline
*/
private fun threadReachedEnd(timelineEvents: List<TimelineEventEntity>): Boolean =
timelineSettings.rootThreadEventId?.let { rootThreadId ->
timelineEvents.firstOrNull { it.eventId == rootThreadId }?.let { true }
} ?: false
/**
* This function is responsible to fetch and store the root event of a thread event
* in order to be able to display the event to the user appropriately
@ -362,7 +389,8 @@ internal class TimelineChunk(private val chunkEntity: ChunkEntity,
val loadMoreResult = try {
if (token == null) {
if (direction == Timeline.Direction.BACKWARDS || !chunkEntity.hasBeenALastForwardChunk()) return LoadMoreResult.REACHED_END
val lastKnownEventId = chunkEntity.sortedTimelineEvents().firstOrNull()?.eventId ?: return LoadMoreResult.FAILURE
val lastKnownEventId = chunkEntity.sortedTimelineEvents(timelineSettings.rootThreadEventId).firstOrNull()?.eventId
?: return LoadMoreResult.FAILURE
val taskParams = FetchTokenAndPaginateTask.Params(roomId, lastKnownEventId, direction.toPaginationDirection(), count)
fetchTokenAndPaginateTask.execute(taskParams).toLoadMoreResult()
} else {
@ -473,6 +501,11 @@ internal class TimelineChunk(private val chunkEntity: ChunkEntity,
onBuiltEvents = this.onBuiltEvents
)
}
private data class LoadedFromStorage(
val threadReachedEnd: Boolean = false,
val numberOfEvents: Int = 0
)
}
private fun RealmQuery<TimelineEventEntity>.offsets(
@ -493,6 +526,19 @@ private fun Timeline.Direction.toPaginationDirection(): PaginationDirection {
return if (this == Timeline.Direction.BACKWARDS) PaginationDirection.BACKWARDS else PaginationDirection.FORWARDS
}
private fun ChunkEntity.sortedTimelineEvents(): RealmResults<TimelineEventEntity> {
return timelineEvents.sort(TimelineEventEntityFields.DISPLAY_INDEX, Sort.DESCENDING)
private fun ChunkEntity.sortedTimelineEvents(rootThreadEventId: String?): RealmResults<TimelineEventEntity> {
return if (rootThreadEventId == null) {
timelineEvents
.sort(TimelineEventEntityFields.DISPLAY_INDEX, Sort.DESCENDING)
} else {
timelineEvents
.where()
.beginGroup()
.equalTo(TimelineEventEntityFields.ROOT.ROOT_THREAD_EVENT_ID, rootThreadEventId)
.or()
.equalTo(TimelineEventEntityFields.ROOT.EVENT_ID, rootThreadEventId)
.endGroup()
.sort(TimelineEventEntityFields.DISPLAY_INDEX, Sort.DESCENDING)
.findAll()
}
}

View file

@ -25,7 +25,6 @@ import org.matrix.android.sdk.api.session.room.send.SendState
import org.matrix.android.sdk.internal.database.helper.addIfNecessary
import org.matrix.android.sdk.internal.database.helper.addStateEvent
import org.matrix.android.sdk.internal.database.helper.addTimelineEvent
import org.matrix.android.sdk.internal.database.helper.merge
import org.matrix.android.sdk.internal.database.helper.updateThreadSummaryIfNeeded
import org.matrix.android.sdk.internal.database.mapper.toEntity
import org.matrix.android.sdk.internal.database.model.ChunkEntity