mirror of
https://github.com/vector-im/riotX-android
synced 2025-10-06 00:02:48 +02:00
Compare commits
3 Commits
develop
...
feature/ar
Author | SHA1 | Date | |
---|---|---|---|
|
8ee8198bf1 | ||
|
17e17abb57 | ||
|
02a34bbc91 |
@@ -47,7 +47,7 @@ android {
|
||||
|
||||
testOptions {
|
||||
// Comment to run on Android 12
|
||||
execution 'ANDROIDX_TEST_ORCHESTRATOR'
|
||||
execution 'ANDROIDX_TEST_ORCHESTRATOR'
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
|
@@ -156,5 +156,5 @@ interface RelationService {
|
||||
* from the backend
|
||||
* @param rootThreadEventId the root thread eventId
|
||||
*/
|
||||
suspend fun fetchThreadTimeline(rootThreadEventId: String): List<Event>
|
||||
suspend fun fetchThreadTimeline(rootThreadEventId: String): Boolean
|
||||
}
|
||||
|
@@ -43,7 +43,7 @@ interface Timeline {
|
||||
/**
|
||||
* This must be called before any other method after creating the timeline. It ensures the underlying database is open
|
||||
*/
|
||||
fun start(rootThreadEventId: String? = null)
|
||||
fun start(rootThreadEventId: String? = null, shouldFetchThreadTimeline: Boolean = true)
|
||||
|
||||
/**
|
||||
* This must be called when you don't need the timeline. It ensures the underlying database get closed.
|
||||
|
@@ -82,7 +82,7 @@ internal fun ChunkEntity.addStateEvent(roomId: String, stateEvent: EventEntity,
|
||||
internal fun ChunkEntity.addTimelineEvent(roomId: String,
|
||||
eventEntity: EventEntity,
|
||||
direction: PaginationDirection,
|
||||
roomMemberContentsByUser: Map<String, RoomMemberContent?>? = null) {
|
||||
roomMemberContentsByUser: Map<String, RoomMemberContent?>) {
|
||||
val eventId = eventEntity.eventId
|
||||
if (timelineEvents.find(eventId) != null) {
|
||||
return
|
||||
@@ -102,7 +102,7 @@ internal fun ChunkEntity.addTimelineEvent(roomId: String,
|
||||
?.also { it.cleanUp(eventEntity.sender) }
|
||||
this.readReceipts = readReceiptsSummaryEntity
|
||||
this.displayIndex = displayIndex
|
||||
val roomMemberContent = roomMemberContentsByUser?.get(senderId)
|
||||
val roomMemberContent = roomMemberContentsByUser[senderId]
|
||||
this.senderAvatar = roomMemberContent?.avatarUrl
|
||||
this.senderName = roomMemberContent?.displayName
|
||||
isUniqueDisplayName = if (roomMemberContent?.displayName != null) {
|
||||
|
@@ -36,7 +36,11 @@ import org.matrix.android.sdk.internal.database.query.whereRoomId
|
||||
* Finds the root thread event and update it with the latest message summary along with the number
|
||||
* of threads included. If there is no root thread event no action is done
|
||||
*/
|
||||
internal fun Map<String, EventEntity>.updateThreadSummaryIfNeeded(roomId: String, realm: Realm, currentUserId: String) {
|
||||
internal fun Map<String, EventEntity>.updateThreadSummaryIfNeeded(roomId: String,
|
||||
realm: Realm,
|
||||
currentUserId: String,
|
||||
shouldUpdateNotifications: Boolean = true
|
||||
) {
|
||||
if (!BuildConfig.THREADING_ENABLED) return
|
||||
|
||||
for ((rootThreadEventId, eventEntity) in this) {
|
||||
@@ -54,8 +58,8 @@ internal fun Map<String, EventEntity>.updateThreadSummaryIfNeeded(roomId: String
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
updateNotificationsNew(roomId, realm, currentUserId)
|
||||
if(shouldUpdateNotifications)
|
||||
updateNotificationsNew(roomId, realm, currentUserId)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -238,56 +238,8 @@ internal class DefaultRelationService @AssistedInject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun fetchThreadTimeline(rootThreadEventId: String): List<Event> {
|
||||
val results = fetchThreadTimelineTask.execute(FetchThreadTimelineTask.Params(roomId, rootThreadEventId))
|
||||
var counter = 0
|
||||
//
|
||||
// monarchy
|
||||
// .awaitTransaction { realm ->
|
||||
// val chunk = ChunkEntity.findLastForwardChunkOfRoom(realm, roomId)
|
||||
//
|
||||
// val optimizedThreadSummaryMap = hashMapOf<String, EventEntity>()
|
||||
// for (event in results.reversed()) {
|
||||
// if (event.eventId == null || event.senderId == null || event.type == null) {
|
||||
// continue
|
||||
// }
|
||||
//
|
||||
// // skip if event already exists
|
||||
// if (EventEntity.where(realm, event.eventId).findFirst() != null) {
|
||||
// counter++
|
||||
// continue
|
||||
// }
|
||||
//
|
||||
// if (event.isEncrypted()) {
|
||||
// decryptIfNeeded(event, roomId)
|
||||
// }
|
||||
//
|
||||
// val ageLocalTs = event.unsignedData?.age?.let { System.currentTimeMillis() - it }
|
||||
// val eventEntity = event.toEntity(roomId, SendState.SYNCED, ageLocalTs).copyToRealmOrIgnore(realm, EventInsertType.INCREMENTAL_SYNC)
|
||||
// if (event.stateKey != null) {
|
||||
// CurrentStateEventEntity.getOrCreate(realm, roomId, event.stateKey, event.type).apply {
|
||||
// eventId = event.eventId
|
||||
// root = eventEntity
|
||||
// }
|
||||
// }
|
||||
// chunk?.addTimelineEvent(roomId, eventEntity, PaginationDirection.FORWARDS)
|
||||
// eventEntity.rootThreadEventId?.let {
|
||||
// // This is a thread event
|
||||
// optimizedThreadSummaryMap[it] = eventEntity
|
||||
// } ?: run {
|
||||
// // This is a normal event or a root thread one
|
||||
// optimizedThreadSummaryMap[eventEntity.eventId] = eventEntity
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// optimizedThreadSummaryMap.updateThreadSummaryIfNeeded(
|
||||
// roomId = roomId,
|
||||
// realm = realm,
|
||||
// currentUserId = userId)
|
||||
// }
|
||||
Timber.i("----> size: ${results.size} | skipped: $counter | threads: ${results.map{ it.eventId}}")
|
||||
|
||||
return results
|
||||
override suspend fun fetchThreadTimeline(rootThreadEventId: String): Boolean {
|
||||
return fetchThreadTimelineTask.execute(FetchThreadTimelineTask.Params(roomId, rootThreadEventId))
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -15,17 +15,45 @@
|
||||
*/
|
||||
package org.matrix.android.sdk.internal.session.room.relation.threads
|
||||
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import io.realm.Realm
|
||||
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.RelationType
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
|
||||
import org.matrix.android.sdk.api.session.room.send.SendState
|
||||
import org.matrix.android.sdk.internal.crypto.CryptoSessionInfoProvider
|
||||
import org.matrix.android.sdk.internal.crypto.DefaultCryptoService
|
||||
import org.matrix.android.sdk.internal.crypto.algorithms.olm.OlmDecryptionResult
|
||||
import org.matrix.android.sdk.internal.database.helper.addTimelineEvent
|
||||
import org.matrix.android.sdk.internal.database.helper.updateThreadSummaryIfNeeded
|
||||
import org.matrix.android.sdk.internal.database.mapper.asDomain
|
||||
import org.matrix.android.sdk.internal.database.mapper.toEntity
|
||||
import org.matrix.android.sdk.internal.database.model.ChunkEntity
|
||||
import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity
|
||||
import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.model.EventEntity
|
||||
import org.matrix.android.sdk.internal.database.model.EventInsertType
|
||||
import org.matrix.android.sdk.internal.database.model.ReactionAggregatedSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.query.copyToRealmOrIgnore
|
||||
import org.matrix.android.sdk.internal.database.query.findLastForwardChunkOfRoom
|
||||
import org.matrix.android.sdk.internal.database.query.getOrCreate
|
||||
import org.matrix.android.sdk.internal.database.query.getOrNull
|
||||
import org.matrix.android.sdk.internal.database.query.where
|
||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||
import org.matrix.android.sdk.internal.di.UserId
|
||||
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||
import org.matrix.android.sdk.internal.network.executeRequest
|
||||
import org.matrix.android.sdk.internal.session.events.getFixedRoomMemberContent
|
||||
import org.matrix.android.sdk.internal.session.room.RoomAPI
|
||||
import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection
|
||||
import org.matrix.android.sdk.internal.task.Task
|
||||
import org.matrix.android.sdk.internal.util.awaitTransaction
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface FetchThreadTimelineTask : Task<FetchThreadTimelineTask.Params, List<Event>> {
|
||||
internal interface FetchThreadTimelineTask : Task<FetchThreadTimelineTask.Params, Boolean> {
|
||||
data class Params(
|
||||
val roomId: String,
|
||||
val rootThreadEventId: String
|
||||
@@ -35,10 +63,13 @@ internal interface FetchThreadTimelineTask : Task<FetchThreadTimelineTask.Params
|
||||
internal class DefaultFetchThreadTimelineTask @Inject constructor(
|
||||
private val roomAPI: RoomAPI,
|
||||
private val globalErrorReceiver: GlobalErrorReceiver,
|
||||
private val cryptoSessionInfoProvider: CryptoSessionInfoProvider
|
||||
private val cryptoSessionInfoProvider: CryptoSessionInfoProvider,
|
||||
@SessionDatabase private val monarchy: Monarchy,
|
||||
@UserId private val userId: String,
|
||||
private val cryptoService: DefaultCryptoService
|
||||
) : FetchThreadTimelineTask {
|
||||
|
||||
override suspend fun execute(params: FetchThreadTimelineTask.Params): List<Event> {
|
||||
override suspend fun execute(params: FetchThreadTimelineTask.Params): Boolean {
|
||||
val isRoomEncrypted = cryptoSessionInfoProvider.isRoomEncrypted(params.roomId)
|
||||
val response = executeRequest(globalErrorReceiver) {
|
||||
roomAPI.getRelations(
|
||||
@@ -49,7 +80,130 @@ internal class DefaultFetchThreadTimelineTask @Inject constructor(
|
||||
limit = 2000
|
||||
)
|
||||
}
|
||||
val threadList = response.chunks + listOfNotNull(response.originalEvent)
|
||||
|
||||
return response.chunks + listOfNotNull(response.originalEvent)
|
||||
return storeNewEventsIfNeeded(threadList, params.roomId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Store new events if they are not already received, and returns weather or not,
|
||||
* a timeline update should be made
|
||||
* @param threadList is the list containing the thread replies
|
||||
* @param roomId the roomId of the the thread
|
||||
* @return
|
||||
*/
|
||||
private suspend fun storeNewEventsIfNeeded(threadList: List<Event>, roomId: String): Boolean {
|
||||
var eventsSkipped = 0
|
||||
monarchy
|
||||
.awaitTransaction { realm ->
|
||||
val chunk = ChunkEntity.findLastForwardChunkOfRoom(realm, roomId)
|
||||
|
||||
val optimizedThreadSummaryMap = hashMapOf<String, EventEntity>()
|
||||
val roomMemberContentsByUser = HashMap<String, RoomMemberContent?>()
|
||||
|
||||
for (event in threadList.reversed()) {
|
||||
|
||||
if (event.eventId == null || event.senderId == null || event.type == null) {
|
||||
eventsSkipped++
|
||||
continue
|
||||
}
|
||||
|
||||
if (EventEntity.where(realm, event.eventId).findFirst() != null) {
|
||||
// Skip if event already exists
|
||||
eventsSkipped++
|
||||
continue
|
||||
}
|
||||
if (event.isEncrypted()) {
|
||||
// Decrypt events that will be stored
|
||||
decryptIfNeeded(event, roomId)
|
||||
}
|
||||
|
||||
handleReaction(realm, event, roomId)
|
||||
|
||||
val ageLocalTs = event.unsignedData?.age?.let { System.currentTimeMillis() - it }
|
||||
val eventEntity = event.toEntity(roomId, SendState.SYNCED, ageLocalTs).copyToRealmOrIgnore(realm, EventInsertType.INCREMENTAL_SYNC)
|
||||
|
||||
// Sender info
|
||||
roomMemberContentsByUser.getOrPut(event.senderId) {
|
||||
// If we don't have any new state on this user, get it from db
|
||||
val rootStateEvent = CurrentStateEventEntity.getOrNull(realm, roomId, event.senderId, EventType.STATE_ROOM_MEMBER)?.root
|
||||
rootStateEvent?.asDomain()?.getFixedRoomMemberContent()
|
||||
}
|
||||
|
||||
chunk?.addTimelineEvent(roomId, eventEntity, PaginationDirection.FORWARDS, roomMemberContentsByUser)
|
||||
eventEntity.rootThreadEventId?.let {
|
||||
// This is a thread event
|
||||
optimizedThreadSummaryMap[it] = eventEntity
|
||||
} ?: run {
|
||||
// This is a normal event or a root thread one
|
||||
optimizedThreadSummaryMap[eventEntity.eventId] = eventEntity
|
||||
}
|
||||
}
|
||||
|
||||
optimizedThreadSummaryMap.updateThreadSummaryIfNeeded(
|
||||
roomId = roomId,
|
||||
realm = realm,
|
||||
currentUserId = userId,
|
||||
shouldUpdateNotifications = false
|
||||
)
|
||||
}
|
||||
Timber.i("----> size: ${threadList.size} | skipped: $eventsSkipped | threads: ${threadList.map { it.eventId }}")
|
||||
|
||||
return eventsSkipped == threadList.size
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the event decryption mechanism for a specific event
|
||||
*/
|
||||
|
||||
private fun decryptIfNeeded(event: Event, roomId: String) {
|
||||
try {
|
||||
// Event from sync does not have roomId, so add it to the event first
|
||||
val result = cryptoService.decryptEvent(event.copy(roomId = roomId), "")
|
||||
event.mxDecryptionResult = OlmDecryptionResult(
|
||||
payload = result.clearEvent,
|
||||
senderKey = result.senderCurve25519Key,
|
||||
keysClaimed = result.claimedEd25519Key?.let { k -> mapOf("ed25519" to k) },
|
||||
forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain
|
||||
)
|
||||
} catch (e: MXCryptoError) {
|
||||
if (e is MXCryptoError.Base) {
|
||||
event.mCryptoError = e.errorType
|
||||
event.mCryptoErrorReason = e.technicalMessage.takeIf { it.isNotEmpty() } ?: e.detailedErrorDescription
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleReaction(realm: Realm,
|
||||
event: Event,
|
||||
roomId: String) {
|
||||
|
||||
val unsignedData = event.unsignedData ?: return
|
||||
val relatedEventId = event.eventId ?: return
|
||||
|
||||
unsignedData.relations?.annotations?.chunk?.forEach { relationChunk ->
|
||||
|
||||
if (relationChunk.type == EventType.REACTION) {
|
||||
val reaction = relationChunk.key
|
||||
Timber.i("----> Annotation found in ${event.eventId} ${relationChunk.key} ")
|
||||
|
||||
val eventSummary = EventAnnotationsSummaryEntity.getOrCreate(realm, roomId, relatedEventId)
|
||||
var sum = eventSummary.reactionsSummary.find { it.key == reaction }
|
||||
|
||||
if (sum == null) {
|
||||
sum = realm.createObject(ReactionAggregatedSummaryEntity::class.java)
|
||||
sum.key = reaction
|
||||
sum.firstTimestamp = event.originServerTs ?: 0
|
||||
Timber.v("Adding synced reaction $reaction")
|
||||
sum.count = 1
|
||||
// reactionEventId not included in the /relations API
|
||||
// sum.sourceEvents.add(reactionEventId)
|
||||
eventSummary.reactionsSummary.add(sum)
|
||||
} else {
|
||||
sum.count += 1
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -45,6 +45,7 @@ import org.matrix.android.sdk.internal.database.query.findAllInRoomWithSendState
|
||||
import org.matrix.android.sdk.internal.database.query.where
|
||||
import org.matrix.android.sdk.internal.database.query.whereRoomId
|
||||
import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask
|
||||
import org.matrix.android.sdk.internal.session.room.relation.threads.FetchThreadTimelineTask
|
||||
import org.matrix.android.sdk.internal.session.sync.handler.room.ReadReceiptHandler
|
||||
import org.matrix.android.sdk.internal.session.sync.handler.room.ThreadsAwarenessHandler
|
||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||
@@ -78,6 +79,7 @@ internal class DefaultTimeline(
|
||||
private val realmSessionProvider: RealmSessionProvider,
|
||||
private val loadRoomMembersTask: LoadRoomMembersTask,
|
||||
private val threadsAwarenessHandler: ThreadsAwarenessHandler,
|
||||
private val fetchThreadTimelineTask: FetchThreadTimelineTask,
|
||||
private val readReceiptHandler: ReadReceiptHandler
|
||||
) : Timeline,
|
||||
TimelineInput.Listener,
|
||||
@@ -130,6 +132,9 @@ internal class DefaultTimeline(
|
||||
return@post
|
||||
}
|
||||
Timber.v("Paginate $direction of $count items")
|
||||
if (isFromThreadTimeline) {
|
||||
Timber.v("----> Paginate $direction of $count items")
|
||||
}
|
||||
val startDisplayIndex = if (direction == Timeline.Direction.BACKWARDS) prevDisplayIndex else nextDisplayIndex
|
||||
val shouldPostSnapshot = paginateInternal(startDisplayIndex, direction, count)
|
||||
if (shouldPostSnapshot) {
|
||||
@@ -150,7 +155,7 @@ internal class DefaultTimeline(
|
||||
}
|
||||
}
|
||||
|
||||
override fun start(rootThreadEventId: String?) {
|
||||
override fun start(rootThreadEventId: String?, shouldFetchThreadTimeline: Boolean) {
|
||||
if (isStarted.compareAndSet(false, true)) {
|
||||
isFromThreadTimeline = rootThreadEventId != null
|
||||
this@DefaultTimeline.rootThreadEventId = rootThreadEventId
|
||||
@@ -174,10 +179,12 @@ internal class DefaultTimeline(
|
||||
}
|
||||
|
||||
timelineEvents = rootThreadEventId?.let {
|
||||
val threadTimelineEvents = TimelineEventEntity
|
||||
if (shouldFetchThreadTimeline) {
|
||||
fetchThreadTimeline(it)
|
||||
}
|
||||
TimelineEventEntity
|
||||
.whereRoomId(realm, roomId = roomId)
|
||||
.equalTo(TimelineEventEntityFields.CHUNK.IS_LAST_FORWARD, true)
|
||||
// .`in`("${TimelineEventEntityFields.CHUNK.TIMELINE_EVENTS}.${TimelineEventEntityFields.EVENT_ID}", arrayOf(it))
|
||||
.beginGroup()
|
||||
.equalTo(TimelineEventEntityFields.ROOT.ROOT_THREAD_EVENT_ID, it)
|
||||
.or()
|
||||
@@ -185,12 +192,6 @@ internal class DefaultTimeline(
|
||||
.endGroup()
|
||||
.sort(TimelineEventEntityFields.DISPLAY_INDEX, Sort.DESCENDING)
|
||||
.findAll()
|
||||
if (threadTimelineEvents.isNullOrEmpty()) {
|
||||
// When there no threads in the last forward chunk get all events and hide them
|
||||
buildEventQuery(realm).sort(TimelineEventEntityFields.DISPLAY_INDEX, Sort.DESCENDING).findAll()
|
||||
} else {
|
||||
threadTimelineEvents
|
||||
}
|
||||
} ?: buildEventQuery(realm).sort(TimelineEventEntityFields.DISPLAY_INDEX, Sort.DESCENDING).findAll()
|
||||
if (isFromThreadTimeline)
|
||||
Timber.i("----> timelineEvents.size: ${timelineEvents.size}")
|
||||
@@ -248,7 +249,7 @@ internal class DefaultTimeline(
|
||||
override fun restartWithEventId(eventId: String?) {
|
||||
dispose()
|
||||
initialEventId = eventId
|
||||
start()
|
||||
start(rootThreadEventId, false)
|
||||
postSnapshot()
|
||||
}
|
||||
|
||||
@@ -343,8 +344,13 @@ internal class DefaultTimeline(
|
||||
val lastCacheEvent = results.lastOrNull()
|
||||
val firstCacheEvent = results.firstOrNull()
|
||||
val chunkEntity = getLiveChunk()
|
||||
if (isFromThreadTimeline)
|
||||
Timber.i("----> results.size: ${results.size} | contains root thread ${results.map { it.eventId }.contains(rootThreadEventId)}")
|
||||
// if (isFromThreadTimeline) {
|
||||
// Timber.i("----> ----> ----> ----> ----> ----> ----> ----> ----> ")
|
||||
// Timber.i("----> lastCacheEvent: ${lastCacheEvent?.eventId}")
|
||||
// Timber.i("----> firstCacheEvent: ${firstCacheEvent?.eventId}")
|
||||
// Timber.i("----> LOADING_STATE results.size: ${results.size} | contains root thread (END BACKWORDS) ${results.map { it.eventId }.contains(rootThreadEventId)}")
|
||||
// Timber.i("----> LOADING_STATE builtEventsIdMap.size: $builtEventsIdMap |: $builtEventsIdMap")
|
||||
// }
|
||||
|
||||
updateState(Timeline.Direction.FORWARDS) { state ->
|
||||
state.copy(
|
||||
@@ -355,7 +361,11 @@ internal class DefaultTimeline(
|
||||
updateState(Timeline.Direction.BACKWARDS) { state ->
|
||||
state.copy(
|
||||
hasMoreInCache = !builtEventsIdMap.containsKey(lastCacheEvent?.eventId),
|
||||
hasReachedEnd = if (isFromThreadTimeline && results.map { it.eventId }.contains(rootThreadEventId)) true else (chunkEntity?.isLastBackward ?: false || lastCacheEvent?.root?.type == EventType.STATE_ROOM_CREATE)
|
||||
hasReachedEnd = if (isFromThreadTimeline && results.map { it.eventId }.contains(rootThreadEventId)) {
|
||||
true
|
||||
} else if (isFromThreadTimeline) {
|
||||
false
|
||||
} else (chunkEntity?.isLastBackward ?: false || lastCacheEvent?.root?.type == EventType.STATE_ROOM_CREATE)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -785,4 +795,28 @@ internal class DefaultTimeline(
|
||||
private fun Timeline.Direction.toPaginationDirection(): PaginationDirection {
|
||||
return if (this == Timeline.Direction.BACKWARDS) PaginationDirection.BACKWARDS else PaginationDirection.FORWARDS
|
||||
}
|
||||
|
||||
// Threads
|
||||
private fun fetchThreadTimeline(rootThreadEventId: String) {
|
||||
val params = FetchThreadTimelineTask.Params(roomId, rootThreadEventId)
|
||||
cancelableBag += fetchThreadTimelineTask
|
||||
.configureWith(params) {
|
||||
this.callback = fetchThreadTimelineCallback()
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
private fun fetchThreadTimelineCallback(): MatrixCallback<Boolean> {
|
||||
return object : MatrixCallback<Boolean> {
|
||||
override fun onSuccess(data: Boolean) {
|
||||
if (!data) {
|
||||
Timber.i("----> New events founds for thread: $rootThreadEventId ")
|
||||
postSnapshot()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -44,6 +44,8 @@ import org.matrix.android.sdk.internal.database.query.where
|
||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||
import org.matrix.android.sdk.internal.di.UserId
|
||||
import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask
|
||||
import org.matrix.android.sdk.internal.session.room.relation.DefaultRelationService
|
||||
import org.matrix.android.sdk.internal.session.room.relation.threads.FetchThreadTimelineTask
|
||||
import org.matrix.android.sdk.internal.session.sync.handler.room.ReadReceiptHandler
|
||||
import org.matrix.android.sdk.internal.session.sync.handler.room.ThreadsAwarenessHandler
|
||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||
@@ -63,6 +65,7 @@ internal class DefaultTimelineService @AssistedInject constructor(
|
||||
private val timelineEventMapper: TimelineEventMapper,
|
||||
private val loadRoomMembersTask: LoadRoomMembersTask,
|
||||
private val threadsAwarenessHandler: ThreadsAwarenessHandler,
|
||||
private val fetchThreadTimelineTask: FetchThreadTimelineTask,
|
||||
private val readReceiptHandler: ReadReceiptHandler
|
||||
) : TimelineService {
|
||||
|
||||
@@ -86,6 +89,7 @@ internal class DefaultTimelineService @AssistedInject constructor(
|
||||
fetchTokenAndPaginateTask = fetchTokenAndPaginateTask,
|
||||
realmSessionProvider = realmSessionProvider,
|
||||
loadRoomMembersTask = loadRoomMembersTask,
|
||||
fetchThreadTimelineTask = fetchThreadTimelineTask,
|
||||
threadsAwarenessHandler = threadsAwarenessHandler,
|
||||
readReceiptHandler = readReceiptHandler
|
||||
)
|
||||
|
@@ -308,17 +308,6 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Fetch all the thread replies for the current thread
|
||||
// */
|
||||
// private fun fetchThreadTimeline() {
|
||||
// initialState.rootThreadEventId?.let {
|
||||
// viewModelScope.launch(Dispatchers.IO) {
|
||||
// room.fetchThreadTimeline(it)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
fun getOtherUserIds() = room.roomSummary()?.otherMemberIds
|
||||
|
||||
fun getRoomSummary() = room.roomSummary()
|
||||
@@ -738,6 +727,7 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||
}
|
||||
|
||||
private fun handleLoadMore(action: RoomDetailAction.LoadMoreTimelineEvents) {
|
||||
|
||||
timeline.paginate(action.direction, PAGINATION_COUNT)
|
||||
}
|
||||
|
||||
|
@@ -451,7 +451,12 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
|
||||
}
|
||||
val readReceipts = receiptsByEvents[event.eventId].orEmpty()
|
||||
return copy(
|
||||
readReceiptsItem = readReceiptsItemFactory.create(event.eventId, readReceipts, callback),
|
||||
readReceiptsItem = readReceiptsItemFactory.create(
|
||||
event.eventId,
|
||||
readReceipts,
|
||||
callback,
|
||||
partialState.isFromThreadTimeline()
|
||||
),
|
||||
formattedDayModel = formattedDayModel,
|
||||
mergedHeaderModel = mergedHeaderModel
|
||||
)
|
||||
|
@@ -26,7 +26,12 @@ import javax.inject.Inject
|
||||
|
||||
class ReadReceiptsItemFactory @Inject constructor(private val avatarRenderer: AvatarRenderer) {
|
||||
|
||||
fun create(eventId: String, readReceipts: List<ReadReceipt>, callback: TimelineEventController.Callback?): ReadReceiptsItem? {
|
||||
fun create(
|
||||
eventId: String,
|
||||
readReceipts: List<ReadReceipt>,
|
||||
callback: TimelineEventController.Callback?,
|
||||
isFromThreadTimeLine: Boolean
|
||||
): ReadReceiptsItem? {
|
||||
if (readReceipts.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
@@ -41,6 +46,7 @@ class ReadReceiptsItemFactory @Inject constructor(private val avatarRenderer: Av
|
||||
.eventId(eventId)
|
||||
.readReceipts(readReceiptsData)
|
||||
.avatarRenderer(avatarRenderer)
|
||||
.shouldHideReadReceipts(isFromThreadTimeLine)
|
||||
.clickListener {
|
||||
callback?.onReadReceiptsClicked(readReceiptsData)
|
||||
}
|
||||
|
@@ -16,6 +16,7 @@
|
||||
|
||||
package im.vector.app.features.home.room.detail.timeline.item
|
||||
|
||||
import androidx.core.view.isVisible
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import com.airbnb.epoxy.EpoxyModelWithHolder
|
||||
@@ -31,6 +32,8 @@ abstract class ReadReceiptsItem : EpoxyModelWithHolder<ReadReceiptsItem.Holder>(
|
||||
|
||||
@EpoxyAttribute lateinit var eventId: String
|
||||
@EpoxyAttribute lateinit var readReceipts: List<ReadReceiptData>
|
||||
@EpoxyAttribute var shouldHideReadReceipts: Boolean = false
|
||||
|
||||
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) lateinit var avatarRenderer: AvatarRenderer
|
||||
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) lateinit var clickListener: ClickListener
|
||||
|
||||
@@ -42,6 +45,7 @@ abstract class ReadReceiptsItem : EpoxyModelWithHolder<ReadReceiptsItem.Holder>(
|
||||
super.bind(holder)
|
||||
holder.readReceiptsView.onClick(clickListener)
|
||||
holder.readReceiptsView.render(readReceipts, avatarRenderer)
|
||||
holder.readReceiptsView.isVisible = !shouldHideReadReceipts
|
||||
}
|
||||
|
||||
override fun unbind(holder: Holder) {
|
||||
|
@@ -110,7 +110,7 @@ class MergedTimelines(
|
||||
secondaryTimeline.removeAllListeners()
|
||||
}
|
||||
|
||||
override fun start(rootThreadEventId: String?) {
|
||||
override fun start(rootThreadEventId: String?,shouldFetchThreadTimeline: Boolean) {
|
||||
mainTimeline.start()
|
||||
secondaryTimeline.start()
|
||||
}
|
||||
|
Reference in New Issue
Block a user