Make TimelineSettings aware of rootThreadEventId and welcome a new Thread mode for the timeline creation

This commit is contained in:
ariskotsomitopoulos 2022-01-10 11:20:31 +02:00
parent 50e51cbe29
commit e541636802
8 changed files with 79 additions and 21 deletions

View file

@ -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
}

View file

@ -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(

View file

@ -20,4 +20,5 @@ internal enum class LoadMoreResult {
REACHED_END,
SUCCESS,
FAILURE
// evenIDS
}

View file

@ -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,13 +208,22 @@ internal class LoadTimelineStrategy(
}
private fun getChunkEntity(realm: Realm): RealmResults<ChunkEntity> {
return if (mode is Mode.Permalink) {
ChunkEntity.findAllIncludingEvents(realm, listOf(mode.originEventId))
} else {
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()
}
}
}
private fun hasReachedLastForward(): Boolean {

View file

@ -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<TimelineEventEntity>) {
if (BuildConfig.THREADING_ENABLED) return
val eventEntityList = offsetResults
.mapNotNull {
it.root

View file

@ -127,7 +127,7 @@ class RoomDetailViewModel @AssistedInject constructor(
private val invisibleEventsSource = BehaviorDataSource<RoomDetailAction.TimelineEventTurnsInvisible>()
private val visibleEventsSource = BehaviorDataSource<RoomDetailAction.TimelineEventTurnsVisible>()
private var timelineEvents = MutableSharedFlow<List<TimelineEvent>>(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)

View file

@ -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)
}

View file

@ -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
)
}
}