From e54163680273deaa41da0fc511913913e69be328 Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Mon, 10 Jan 2022 11:20:31 +0200 Subject: [PATCH] Make TimelineSettings aware of rootThreadEventId and welcome a new Thread mode for the timeline creation --- .../session/room/timeline/TimelineSettings.kt | 13 ++++++-- .../session/room/timeline/DefaultTimeline.kt | 16 +++++----- .../session/room/timeline/LoadMoreResult.kt | 1 + .../room/timeline/LoadTimelineStrategy.kt | 31 +++++++++++++++---- .../session/room/timeline/TimelineChunk.kt | 21 ++++++++++++- .../home/room/detail/RoomDetailViewModel.kt | 2 +- .../timeline/factory/TimelineFactory.kt | 10 ++++-- .../helper/TimelineSettingsFactory.kt | 6 ++-- 8 files changed, 79 insertions(+), 21 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineSettings.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineSettings.kt index ceffedb234..6548453c8a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineSettings.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineSettings.kt @@ -27,5 +27,14 @@ data class TimelineSettings( /** * If true, will build read receipts for each event. */ - val buildReadReceipts: Boolean = true -) + val buildReadReceipts: Boolean = true, + /** + * The root thread eventId if this is a thread timeline, or null if this is NOT a thread timeline + */ + val rootThreadEventId: String? = null) { + + /** + * Returns true if this is a thread timeline or false otherwise + */ + fun isThreadTimeline() = rootThreadEventId != null +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt index 483851ebd7..03ea2fdcb5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt @@ -136,7 +136,7 @@ internal class DefaultTimeline(private val roomId: String, ensureReadReceiptAreLoaded(realm) backgroundRealm.set(realm) listenToPostSnapshotSignals() - openAround(initialEventId) + openAround(initialEventId, rootThreadEventId) postSnapshot() } } @@ -157,7 +157,7 @@ internal class DefaultTimeline(private val roomId: String, override fun restartWithEventId(eventId: String?) { timelineScope.launch { - openAround(eventId) + openAround(eventId,rootThreadEventId) postSnapshot() } } @@ -226,18 +226,20 @@ internal class DefaultTimeline(private val roomId: String, return true } - private suspend fun openAround(eventId: String?) = withContext(timelineDispatcher) { + private suspend fun openAround(eventId: String?, rootThreadEventId: String?) = withContext(timelineDispatcher) { val baseLogMessage = "openAround(eventId: $eventId)" Timber.v("$baseLogMessage started") if (!isStarted.get()) { throw IllegalStateException("You should call start before using timeline") } strategy.onStop() - strategy = if (eventId == null) { - buildStrategy(LoadTimelineStrategy.Mode.Live) - } else { - buildStrategy(LoadTimelineStrategy.Mode.Permalink(eventId)) + + strategy = when { + rootThreadEventId != null -> buildStrategy(LoadTimelineStrategy.Mode.Thread(rootThreadEventId)) + eventId == null -> buildStrategy(LoadTimelineStrategy.Mode.Live) + else -> buildStrategy(LoadTimelineStrategy.Mode.Permalink(eventId)) } + initPaginationStates(eventId) strategy.onStart() loadMore( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LoadMoreResult.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LoadMoreResult.kt index c419e8325e..2949e35bd3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LoadMoreResult.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LoadMoreResult.kt @@ -20,4 +20,5 @@ internal enum class LoadMoreResult { REACHED_END, SUCCESS, FAILURE + // evenIDS } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LoadTimelineStrategy.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LoadTimelineStrategy.kt index 528b564e8b..4aee1b1a30 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LoadTimelineStrategy.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LoadTimelineStrategy.kt @@ -51,6 +51,7 @@ internal class LoadTimelineStrategy( sealed interface Mode { object Live : Mode data class Permalink(val originEventId: String) : Mode + data class Thread(val rootThreadEventId: String) : Mode fun originEventId(): String? { return if (this is Permalink) { @@ -59,6 +60,14 @@ internal class LoadTimelineStrategy( null } } + +// fun getRootThreadEventId(): String? { +// return if (this is Thread) { +// rootThreadEventId +// } else { +// null +// } +// } } data class Dependencies( @@ -162,6 +171,7 @@ internal class LoadTimelineStrategy( } suspend fun loadMore(count: Int, direction: Timeline.Direction, fetchOnServerIfNeeded: Boolean = true): LoadMoreResult { + /// if (mode is Mode.Permalink && timelineChunk == null) { val params = GetContextOfEventTask.Params(roomId, mode.originEventId) try { @@ -198,12 +208,21 @@ internal class LoadTimelineStrategy( } private fun getChunkEntity(realm: Realm): RealmResults { - return if (mode is Mode.Permalink) { - ChunkEntity.findAllIncludingEvents(realm, listOf(mode.originEventId)) - } else { - ChunkEntity.where(realm, roomId) - .equalTo(ChunkEntityFields.IS_LAST_FORWARD, true) - .findAll() + + return when (mode) { + is Mode.Live -> { + ChunkEntity.where(realm, roomId) + .equalTo(ChunkEntityFields.IS_LAST_FORWARD, true) + .findAll() + } + is Mode.Permalink -> { + ChunkEntity.findAllIncludingEvents(realm, listOf(mode.originEventId)) + } + is Mode.Thread -> { + ChunkEntity.where(realm, roomId) + .equalTo(ChunkEntityFields.IS_LAST_FORWARD, true) + .findAll() + } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineChunk.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineChunk.kt index 14cba2a4b8..e05def3805 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineChunk.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineChunk.kt @@ -23,6 +23,7 @@ import io.realm.RealmQuery import io.realm.RealmResults import io.realm.Sort import kotlinx.coroutines.CompletableDeferred +import org.matrix.android.sdk.BuildConfig import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.session.events.model.EventType @@ -271,7 +272,24 @@ internal class TimelineChunk(private val chunkEntity: ChunkEntity, private suspend fun loadFromStorage(count: Int, direction: Timeline.Direction): Int { val displayIndex = getNextDisplayIndex(direction) ?: return 0 val baseQuery = timelineEventEntities.where() - val timelineEvents = 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 { + baseQuery + .offsets(direction, count, displayIndex) + .findAll() + .orEmpty() + } + if (timelineEvents.isEmpty()) return 0 fetchRootThreadEventsIfNeeded(timelineEvents) if (direction == Timeline.Direction.FORWARDS) { @@ -299,6 +317,7 @@ internal class TimelineChunk(private val chunkEntity: ChunkEntity, * in order to be able to display the event to the user appropriately */ private suspend fun fetchRootThreadEventsIfNeeded(offsetResults: List) { + if (BuildConfig.THREADING_ENABLED) return val eventEntityList = offsetResults .mapNotNull { it.root diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index f60df814e5..4ee628ff16 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -127,7 +127,7 @@ class RoomDetailViewModel @AssistedInject constructor( private val invisibleEventsSource = BehaviorDataSource() private val visibleEventsSource = BehaviorDataSource() private var timelineEvents = MutableSharedFlow>(0) - val timeline = timelineFactory.createTimeline(viewModelScope, room, eventId) + val timeline = timelineFactory.createTimeline(viewModelScope, room, eventId, initialState.rootThreadEventId) // Same lifecycle than the ViewModel (survive to screen rotation) val previewUrlRetriever = PreviewUrlRetriever(session, viewModelScope) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineFactory.kt index b57e39b3cf..3ec1366131 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineFactory.kt @@ -35,8 +35,14 @@ private val secondaryTimelineAllowedTypes = listOf( class TimelineFactory @Inject constructor(private val session: Session, private val timelineSettingsFactory: TimelineSettingsFactory) { - fun createTimeline(coroutineScope: CoroutineScope, mainRoom: Room, eventId: String?): Timeline { - val settings = timelineSettingsFactory.create() + fun createTimeline( + coroutineScope: CoroutineScope, + mainRoom: Room, + eventId: String?, + rootThreadEventId: String? + ): Timeline { + val settings = timelineSettingsFactory.create(rootThreadEventId) + if (!session.vectorCallService.protocolChecker.supportVirtualRooms) { return mainRoom.createTimeline(eventId, settings) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineSettingsFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineSettingsFactory.kt index 3aee65bf19..8b7dcc9c72 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineSettingsFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineSettingsFactory.kt @@ -22,9 +22,11 @@ import javax.inject.Inject class TimelineSettingsFactory @Inject constructor(private val userPreferencesProvider: UserPreferencesProvider) { - fun create(): TimelineSettings { + fun create(rootThreadEventId: String?): TimelineSettings { return TimelineSettings( initialSize = 30, - buildReadReceipts = userPreferencesProvider.shouldShowReadReceipts()) + buildReadReceipts = userPreferencesProvider.shouldShowReadReceipts(), + rootThreadEventId = rootThreadEventId + ) } }