From 283f32479db0a18f7f746a44c7461a7389108045 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 3 Jul 2020 21:11:54 +0200 Subject: [PATCH] Rebranch timeline + continue clean up strategy --- .../internal/database/DatabaseCleaner.kt | 98 +++++++++++++++++++ .../internal/session/DefaultSession.kt | 43 -------- .../android/internal/session/SessionModule.kt | 5 + .../internal/session/room/RoomModule.kt | 6 +- .../session/room/timeline/DefaultTimeline.kt | 89 ++++++++--------- .../room/timeline/DefaultTimelineService.kt | 4 +- ...teTask.kt => FetchTokenAndPaginateTask.kt} | 30 ++++-- .../timeline/TimelineHiddenReadReceipts.kt | 2 +- 8 files changed, 169 insertions(+), 108 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/DatabaseCleaner.kt rename matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/{FetchNextTokenAndPaginateTask.kt => FetchTokenAndPaginateTask.kt} (67%) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/DatabaseCleaner.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/DatabaseCleaner.kt new file mode 100644 index 0000000000..a13cd6e078 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/DatabaseCleaner.kt @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.database + +import im.vector.matrix.android.internal.database.helper.nextDisplayIndex +import im.vector.matrix.android.internal.database.model.ChunkEntity +import im.vector.matrix.android.internal.database.model.ChunkEntityFields +import im.vector.matrix.android.internal.database.model.EventEntity +import im.vector.matrix.android.internal.database.model.RoomEntity +import im.vector.matrix.android.internal.database.model.TimelineEventEntity +import im.vector.matrix.android.internal.database.model.TimelineEventEntityFields +import im.vector.matrix.android.internal.di.SessionDatabase +import im.vector.matrix.android.internal.session.SessionLifecycleObserver +import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection +import im.vector.matrix.android.internal.task.TaskExecutor +import io.realm.Realm +import io.realm.RealmConfiguration +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import timber.log.Timber +import javax.inject.Inject + +private const val MAX_NUMBER_OF_EVENTS = 35_000L +private const val MIN_NUMBER_OF_EVENTS_BY_CHUNK = 300 + +/** + * This class makes sure to stay under a maximum number of events as it makes Realm to be unusable when listening to events + * when the database is getting too big. + */ +internal class DatabaseCleaner @Inject constructor(@SessionDatabase private val realmConfiguration: RealmConfiguration, + private val taskExecutor: TaskExecutor) : SessionLifecycleObserver { + + override fun onStart() { + taskExecutor.executorScope.launch(Dispatchers.Default) { + awaitTransaction(realmConfiguration) { realm -> + val allRooms = realm.where(RoomEntity::class.java).findAll() + Timber.v("There are ${allRooms.size} rooms in this session") + cleanUp(realm, MAX_NUMBER_OF_EVENTS / 2L) + } + } + } + + private suspend fun cleanUp(realm: Realm, threshold: Long) { + val numberOfEvents = realm.where(EventEntity::class.java).findAll().size + val numberOfTimelineEvents = realm.where(TimelineEventEntity::class.java).findAll().size + Timber.v("Number of events in db: $numberOfEvents | Number of timeline events in db: $numberOfTimelineEvents") + if (threshold <= MIN_NUMBER_OF_EVENTS_BY_CHUNK || numberOfTimelineEvents < MAX_NUMBER_OF_EVENTS) { + Timber.v("Db is low enough") + } else { + val thresholdChunks = realm.where(ChunkEntity::class.java) + .greaterThan(ChunkEntityFields.NUMBER_OF_TIMELINE_EVENTS, threshold) + .findAll() + + Timber.v("There are ${thresholdChunks.size} chunks to clean with more than $threshold events") + for (chunk in thresholdChunks) { + val maxDisplayIndex = chunk.nextDisplayIndex(PaginationDirection.FORWARDS) + val thresholdDisplayIndex = maxDisplayIndex - threshold + val eventsToRemove = chunk.timelineEvents.where().lessThan(TimelineEventEntityFields.DISPLAY_INDEX, thresholdDisplayIndex).findAll() + Timber.v("There are ${eventsToRemove.size} events to clean in chunk: ${chunk.identifier()} from room ${chunk.room?.first()?.roomId}") + chunk.numberOfTimelineEvents = chunk.numberOfTimelineEvents - eventsToRemove.size + eventsToRemove.forEach { + val canDeleteRoot = it.root?.stateKey == null + if (canDeleteRoot) { + it.root?.deleteFromRealm() + } + it.readReceipts?.readReceipts?.deleteAllFromRealm() + it.readReceipts?.deleteFromRealm() + it.annotations?.apply { + editSummary?.deleteFromRealm() + pollResponseSummary?.deleteFromRealm() + referencesSummaryEntity?.deleteFromRealm() + reactionsSummary.deleteAllFromRealm() + } + it.annotations?.deleteFromRealm() + it.readReceipts?.deleteFromRealm() + it.deleteFromRealm() + } + // We reset the prevToken so we will need to fetch again. + chunk.prevToken = null + } + cleanUp(realm, (threshold / 1.5).toLong()) + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt index 466ad6fc7d..e7ab2a7926 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt @@ -164,49 +164,6 @@ internal class DefaultSession @Inject constructor( } eventBus.register(this) timelineEventDecryptor.start() - - taskExecutor.executorScope.launch(Dispatchers.Default) { - awaitTransaction(realmConfiguration) { realm -> - val allRooms = realm.where(RoomEntity::class.java).findAll() - val numberOfEvents = realm.where(EventEntity::class.java).findAll().size - val numberOfTimelineEvents = realm.where(TimelineEventEntity::class.java).findAll().size - Timber.v("Number of events in db: $numberOfEvents | Number of timeline events in db: $numberOfTimelineEvents") - Timber.v("Number of rooms in db: ${allRooms.size}") - if (numberOfTimelineEvents < 30_000L) { - Timber.v("Db is low enough") - } else { - val hugeChunks = realm.where(ChunkEntity::class.java).greaterThan(ChunkEntityFields.NUMBER_OF_TIMELINE_EVENTS, 250).findAll() - Timber.v("There are ${hugeChunks.size} chunks to clean") - /* - for (chunk in hugeChunks) { - val maxDisplayIndex = chunk.nextDisplayIndex(PaginationDirection.FORWARDS) - val thresholdDisplayIndex = maxDisplayIndex - 250 - val eventsToRemove = chunk.timelineEvents.where().lessThan(TimelineEventEntityFields.DISPLAY_INDEX, thresholdDisplayIndex).findAll() - Timber.v("There are ${eventsToRemove.size} events to clean in chunk: ${chunk.identifier()} from room ${chunk.room?.first()?.roomId}") - chunk.numberOfTimelineEvents = chunk.numberOfTimelineEvents - eventsToRemove.size - eventsToRemove.forEach { - val canDeleteRoot = it.root?.stateKey == null - if (canDeleteRoot) { - it.root?.deleteFromRealm() - } - it.readReceipts?.readReceipts?.deleteAllFromRealm() - it.readReceipts?.deleteFromRealm() - it.annotations?.apply { - editSummary?.deleteFromRealm() - pollResponseSummary?.deleteFromRealm() - referencesSummaryEntity?.deleteFromRealm() - reactionsSummary.deleteAllFromRealm() - } - it.annotations?.deleteFromRealm() - it.readReceipts?.deleteFromRealm() - it.deleteFromRealm() - } - } - - */ - } - } - } } override fun requireBackgroundSync() { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt index b8aaddd086..8aabd709ab 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt @@ -40,6 +40,7 @@ import im.vector.matrix.android.api.session.typing.TypingUsersTracker import im.vector.matrix.android.internal.crypto.crosssigning.ShieldTrustUpdater import im.vector.matrix.android.internal.crypto.secrets.DefaultSharedSecretStorageService import im.vector.matrix.android.internal.crypto.verification.VerificationMessageProcessor +import im.vector.matrix.android.internal.database.DatabaseCleaner import im.vector.matrix.android.internal.database.EventInsertLiveObserver import im.vector.matrix.android.internal.database.SessionRealmConfigurationFactory import im.vector.matrix.android.internal.di.Authenticated @@ -340,6 +341,10 @@ internal abstract class SessionModule { @IntoSet abstract fun bindIdentityService(observer: DefaultIdentityService): SessionLifecycleObserver + @Binds + @IntoSet + abstract fun bindDatabaseCleaner(observer: DatabaseCleaner): SessionLifecycleObserver + @Binds abstract fun bindInitialSyncProgressService(service: DefaultInitialSyncProgressService): InitialSyncProgressService diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt index ca59f9b7bb..7fa9c1526a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt @@ -62,10 +62,10 @@ import im.vector.matrix.android.internal.session.room.tags.AddTagToRoomTask import im.vector.matrix.android.internal.session.room.tags.DefaultAddTagToRoomTask import im.vector.matrix.android.internal.session.room.tags.DefaultDeleteTagFromRoomTask import im.vector.matrix.android.internal.session.room.tags.DeleteTagFromRoomTask -import im.vector.matrix.android.internal.session.room.timeline.DefaultFetchNextTokenAndPaginateTask +import im.vector.matrix.android.internal.session.room.timeline.DefaultFetchTokenAndPaginateTask import im.vector.matrix.android.internal.session.room.timeline.DefaultGetContextOfEventTask import im.vector.matrix.android.internal.session.room.timeline.DefaultPaginationTask -import im.vector.matrix.android.internal.session.room.timeline.FetchNextTokenAndPaginateTask +import im.vector.matrix.android.internal.session.room.timeline.FetchTokenAndPaginateTask import im.vector.matrix.android.internal.session.room.timeline.GetContextOfEventTask import im.vector.matrix.android.internal.session.room.timeline.PaginationTask import im.vector.matrix.android.internal.session.room.typing.DefaultSendTypingTask @@ -176,7 +176,7 @@ internal abstract class RoomModule { abstract fun bindPaginationTask(task: DefaultPaginationTask): PaginationTask @Binds - abstract fun bindFetchNextTokenAndPaginateTask(task: DefaultFetchNextTokenAndPaginateTask): FetchNextTokenAndPaginateTask + abstract fun bindFetchNextTokenAndPaginateTask(task: DefaultFetchTokenAndPaginateTask): FetchTokenAndPaginateTask @Binds abstract fun bindFetchEditHistoryTask(task: DefaultFetchEditHistoryTask): FetchEditHistoryTask diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt index c93d1e7e4b..1822587972 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt @@ -29,17 +29,14 @@ import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.room.timeline.TimelineSettings import im.vector.matrix.android.api.util.CancelableBag import im.vector.matrix.android.internal.database.mapper.TimelineEventMapper -import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.model.ChunkEntity import im.vector.matrix.android.internal.database.model.ChunkEntityFields -import im.vector.matrix.android.internal.database.model.EventAnnotationsSummaryEntity import im.vector.matrix.android.internal.database.model.RoomEntity import im.vector.matrix.android.internal.database.model.TimelineEventEntity import im.vector.matrix.android.internal.database.model.TimelineEventEntityFields import im.vector.matrix.android.internal.database.query.TimelineEventFilter import im.vector.matrix.android.internal.database.query.findAllInRoomWithSendStates import im.vector.matrix.android.internal.database.query.where -import im.vector.matrix.android.internal.database.query.whereInRoom import im.vector.matrix.android.internal.database.query.whereRoomId import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith @@ -72,7 +69,7 @@ internal class DefaultTimeline( private val realmConfiguration: RealmConfiguration, private val taskExecutor: TaskExecutor, private val contextOfEventTask: GetContextOfEventTask, - private val fetchNextTokenAndPaginateTask: FetchNextTokenAndPaginateTask, + private val fetchTokenAndPaginateTask: FetchTokenAndPaginateTask, private val paginationTask: PaginationTask, private val timelineEventMapper: TimelineEventMapper, private val settings: TimelineSettings, @@ -98,9 +95,7 @@ internal class DefaultTimeline( private lateinit var nonFilteredEvents: RealmResults private lateinit var filteredEvents: RealmResults - private lateinit var eventRelations: RealmResults - - private var roomEntity: RoomEntity? = null + private lateinit var sendingEvents: RealmResults private var prevDisplayIndex: Int? = null private var nextDisplayIndex: Int? = null @@ -119,23 +114,10 @@ internal class DefaultTimeline( if (!results.isLoaded || !results.isValid) { return@OrderedRealmCollectionChangeListener } + results.createSnapshot() handleUpdates(results, changeSet) } - private val relationsListener = OrderedRealmCollectionChangeListener> { collection, changeSet -> - var hasChange = false - - (changeSet.insertions + changeSet.changes).forEach { - val eventRelations = collection[it] - if (eventRelations != null) { - hasChange = rebuildEvent(eventRelations.eventId) { te -> - te.copy(annotations = eventRelations.asDomain()) - } || hasChange - } - } - if (hasChange) postSnapshot() - } - // Public methods ****************************************************************************** override fun paginate(direction: Timeline.Direction, count: Int) { @@ -173,15 +155,23 @@ internal class DefaultTimeline( val realm = Realm.getInstance(realmConfiguration) backgroundRealm.set(realm) - roomEntity = RoomEntity.where(realm, roomId = roomId).findFirst() + val roomEntity = RoomEntity.where(realm, roomId = roomId).findFirst() + ?: throw IllegalStateException("Can't open a timeline without a room") + + sendingEvents = roomEntity.sendingTimelineEvents.where().filterEventsWithSettings().findAll() + sendingEvents.addChangeListener { events -> + // Remove in memory as soon as they are known by database + events.forEach { te -> + inMemorySendingEvents.removeAll { te.eventId == it.eventId } + } + postSnapshot() + } nonFilteredEvents = buildEventQuery(realm).sort(TimelineEventEntityFields.DISPLAY_INDEX, Sort.DESCENDING).findAll() filteredEvents = nonFilteredEvents.where() .filterEventsWithSettings() .findAll() + filteredEvents.addChangeListener(eventsChangeListener) handleInitialLoad() - eventRelations = EventAnnotationsSummaryEntity.whereInRoom(realm, roomId) - .findAllAsync() - if (settings.shouldHandleHiddenReadReceipts()) { hiddenReadReceipts.start(realm, filteredEvents, nonFilteredEvents, this) } @@ -202,9 +192,8 @@ internal class DefaultTimeline( cancelableBag.cancel() BACKGROUND_HANDLER.removeCallbacksAndMessages(null) BACKGROUND_HANDLER.post { - roomEntity?.sendingTimelineEvents?.removeAllChangeListeners() - if (this::eventRelations.isInitialized) { - eventRelations.removeAllChangeListeners() + if (this::sendingEvents.isInitialized) { + sendingEvents.removeAllChangeListeners() } if (this::nonFilteredEvents.isInitialized) { nonFilteredEvents.removeAllChangeListeners() @@ -303,7 +292,7 @@ internal class DefaultTimeline( listeners.clear() } - // TimelineHiddenReadReceipts.Delegate +// TimelineHiddenReadReceipts.Delegate override fun rebuildEvent(eventId: String, readReceipts: List): Boolean { return rebuildEvent(eventId) { te -> @@ -336,7 +325,7 @@ internal class DefaultTimeline( } } - // Private methods ***************************************************************************** +// Private methods ***************************************************************************** private fun rebuildEvent(eventId: String, builder: (TimelineEvent) -> TimelineEvent): Boolean { return builtEventsIdMap[eventId]?.let { builtIndex -> @@ -400,20 +389,16 @@ internal class DefaultTimeline( } private fun buildSendingEvents(): List { - val sendingEvents = ArrayList() + val builtSendingEvents = ArrayList() if (hasReachedEnd(Timeline.Direction.FORWARDS) && !hasMoreInCache(Timeline.Direction.FORWARDS)) { - sendingEvents.addAll(inMemorySendingEvents.filterEventsWithSettings()) - roomEntity?.sendingTimelineEvents - ?.where() - ?.filterEventsWithSettings() - ?.findAll() - ?.forEach { timelineEventEntity -> - if (sendingEvents.find { it.eventId == timelineEventEntity.eventId } == null) { - sendingEvents.add(timelineEventMapper.map(timelineEventEntity)) - } - } + builtSendingEvents.addAll(inMemorySendingEvents.filterEventsWithSettings()) + sendingEvents.forEach { timelineEventEntity -> + if (builtSendingEvents.find { it.eventId == timelineEventEntity.eventId } == null) { + builtSendingEvents.add(timelineEventMapper.map(timelineEventEntity)) + } + } } - return sendingEvents + return builtSendingEvents } private fun canPaginate(direction: Timeline.Direction): Boolean { @@ -514,19 +499,25 @@ internal class DefaultTimeline( val currentChunk = getLiveChunk() val token = if (direction == Timeline.Direction.BACKWARDS) currentChunk?.prevToken else currentChunk?.nextToken if (token == null) { - if (direction == Timeline.Direction.FORWARDS && currentChunk?.hasBeenALastForwardChunk().orFalse()) { - // We are in the case that next event exists, but we do not know the next token. - // Fetch (again) the last event to get a nextToken - val lastKnownEventId = nonFilteredEvents.firstOrNull()?.eventId + if (direction == Timeline.Direction.BACKWARDS || + (direction == Timeline.Direction.FORWARDS && currentChunk?.hasBeenALastForwardChunk().orFalse())) { + // We are in the case where event exists, but we do not know the token. + // Fetch (again) the last event to get a token + val lastKnownEventId = if (direction == Timeline.Direction.FORWARDS) { + nonFilteredEvents.firstOrNull()?.eventId + } else { + nonFilteredEvents.lastOrNull()?.eventId + } if (lastKnownEventId == null) { updateState(direction) { it.copy(isPaginating = false, requestedPaginationCount = 0) } } else { - val params = FetchNextTokenAndPaginateTask.Params( + val params = FetchTokenAndPaginateTask.Params( roomId = roomId, limit = limit, + direction = direction.toPaginationDirection(), lastKnownEventId = lastKnownEventId ) - cancelableBag += fetchNextTokenAndPaginateTask + cancelableBag += fetchTokenAndPaginateTask .configureWith(params) { this.callback = createPaginationCallback(limit, direction) } @@ -755,7 +746,7 @@ internal class DefaultTimeline( } } -// Extension methods *************************************************************************** + // Extension methods *************************************************************************** private fun Timeline.Direction.toPaginationDirection(): PaginationDirection { return if (this == Timeline.Direction.BACKWARDS) PaginationDirection.BACKWARDS else PaginationDirection.FORWARDS diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineService.kt index 449061c2f7..5723568197 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineService.kt @@ -43,7 +43,7 @@ internal class DefaultTimelineService @AssistedInject constructor(@Assisted priv private val contextOfEventTask: GetContextOfEventTask, private val eventDecryptor: TimelineEventDecryptor, private val paginationTask: PaginationTask, - private val fetchNextTokenAndPaginateTask: FetchNextTokenAndPaginateTask, + private val fetchTokenAndPaginateTask: FetchTokenAndPaginateTask, private val timelineEventMapper: TimelineEventMapper, private val readReceiptsSummaryMapper: ReadReceiptsSummaryMapper ) : TimelineService { @@ -66,7 +66,7 @@ internal class DefaultTimelineService @AssistedInject constructor(@Assisted priv hiddenReadReceipts = TimelineHiddenReadReceipts(readReceiptsSummaryMapper, roomId, settings), eventBus = eventBus, eventDecryptor = eventDecryptor, - fetchNextTokenAndPaginateTask = fetchNextTokenAndPaginateTask + fetchTokenAndPaginateTask = fetchTokenAndPaginateTask ) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/FetchNextTokenAndPaginateTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/FetchTokenAndPaginateTask.kt similarity index 67% rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/FetchNextTokenAndPaginateTask.kt rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/FetchTokenAndPaginateTask.kt index 6579e0031a..3c1f6b18bd 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/FetchNextTokenAndPaginateTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/FetchTokenAndPaginateTask.kt @@ -28,38 +28,48 @@ import im.vector.matrix.android.internal.util.awaitTransaction import org.greenrobot.eventbus.EventBus import javax.inject.Inject -internal interface FetchNextTokenAndPaginateTask : Task { +internal interface FetchTokenAndPaginateTask : Task { data class Params( val roomId: String, val lastKnownEventId: String, + val direction: PaginationDirection, val limit: Int ) } -internal class DefaultFetchNextTokenAndPaginateTask @Inject constructor( +internal class DefaultFetchTokenAndPaginateTask @Inject constructor( private val roomAPI: RoomAPI, @SessionDatabase private val monarchy: Monarchy, private val filterRepository: FilterRepository, private val paginationTask: PaginationTask, private val eventBus: EventBus -) : FetchNextTokenAndPaginateTask { +) : FetchTokenAndPaginateTask { - override suspend fun execute(params: FetchNextTokenAndPaginateTask.Params): TokenChunkEventPersistor.Result { + override suspend fun execute(params: FetchTokenAndPaginateTask.Params): TokenChunkEventPersistor.Result { val filter = filterRepository.getRoomFilter() val response = executeRequest(eventBus) { apiCall = roomAPI.getContextOfEvent(params.roomId, params.lastKnownEventId, 0, filter) } - if (response.end == null) { - throw IllegalStateException("No next token found") + val fromToken = if (params.direction == PaginationDirection.FORWARDS) { + response.end + } else { + response.start } - monarchy.awaitTransaction { - ChunkEntity.findIncludingEvent(it, params.lastKnownEventId)?.nextToken = response.end + ?: throw IllegalStateException("No token found") + + monarchy.awaitTransaction { realm -> + val chunkToUpdate = ChunkEntity.findIncludingEvent(realm, params.lastKnownEventId) + if (params.direction == PaginationDirection.FORWARDS) { + chunkToUpdate?.nextToken = fromToken + } else { + chunkToUpdate?.prevToken = fromToken + } } val paginationParams = PaginationTask.Params( roomId = params.roomId, - from = response.end, - direction = PaginationDirection.FORWARDS, + from = fromToken, + direction = params.direction, limit = params.limit ) return paginationTask.execute(paginationParams) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineHiddenReadReceipts.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineHiddenReadReceipts.kt index ae10ccf80d..ddfa7e91fe 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineHiddenReadReceipts.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineHiddenReadReceipts.kt @@ -125,7 +125,7 @@ internal class TimelineHiddenReadReceipts constructor(private val readReceiptsSu .isNotEmpty(ReadReceiptsSummaryEntityFields.READ_RECEIPTS.`$`) .filterReceiptsWithSettings() .findAllAsync() - //.also { it.addChangeListener(hiddenReadReceiptsListener) } + .also { it.addChangeListener(hiddenReadReceiptsListener) } } /**