Handle latest thread message & root thread edition to update thread summary and thread list appropriately
This commit is contained in:
parent
92d082c26a
commit
358a7d0ec4
|
@ -86,6 +86,13 @@ interface TimelineService {
|
||||||
*/
|
*/
|
||||||
fun isUserParticipatingInThread(rootThreadEventId: String): Boolean
|
fun isUserParticipatingInThread(rootThreadEventId: String): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enhance the thread list with the edited events if needed
|
||||||
|
* @return the [LiveData] of [TimelineEvent]
|
||||||
|
*/
|
||||||
|
fun mapEventsWithEdition(threads: List<TimelineEvent>): List<TimelineEvent>
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks the current thread as read. This is a local implementation
|
* Marks the current thread as read. This is a local implementation
|
||||||
* @param rootThreadEventId the eventId of the current thread
|
* @param rootThreadEventId the eventId of the current thread
|
||||||
|
|
|
@ -29,5 +29,6 @@ data class ThreadDetails(
|
||||||
val threadSummaryLatestTextMessage: String? = null,
|
val threadSummaryLatestTextMessage: String? = null,
|
||||||
val lastMessageTimestamp: Long? = null,
|
val lastMessageTimestamp: Long? = null,
|
||||||
var threadNotificationState: ThreadNotificationState = ThreadNotificationState.NO_NEW_MESSAGE,
|
var threadNotificationState: ThreadNotificationState = ThreadNotificationState.NO_NEW_MESSAGE,
|
||||||
val isThread: Boolean = false
|
val isThread: Boolean = false,
|
||||||
|
val lastRootThreadEdition: String? = null
|
||||||
)
|
)
|
||||||
|
|
|
@ -19,9 +19,11 @@ package org.matrix.android.sdk.internal.database.helper
|
||||||
import io.realm.Realm
|
import io.realm.Realm
|
||||||
import io.realm.RealmQuery
|
import io.realm.RealmQuery
|
||||||
import io.realm.Sort
|
import io.realm.Sort
|
||||||
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||||
import org.matrix.android.sdk.api.session.threads.ThreadNotificationState
|
import org.matrix.android.sdk.api.session.threads.ThreadNotificationState
|
||||||
import org.matrix.android.sdk.internal.database.mapper.asDomain
|
import org.matrix.android.sdk.internal.database.mapper.asDomain
|
||||||
import org.matrix.android.sdk.internal.database.model.ChunkEntity
|
import org.matrix.android.sdk.internal.database.model.ChunkEntity
|
||||||
|
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.EventEntity
|
||||||
import org.matrix.android.sdk.internal.database.model.ReadReceiptEntity
|
import org.matrix.android.sdk.internal.database.model.ReadReceiptEntity
|
||||||
import org.matrix.android.sdk.internal.database.model.TimelineEventEntity
|
import org.matrix.android.sdk.internal.database.model.TimelineEventEntity
|
||||||
|
@ -88,17 +90,6 @@ internal fun EventEntity.markEventAsRoot(
|
||||||
threadSummaryLatestMessage = latestMessageTimelineEventEntity
|
threadSummaryLatestMessage = latestMessageTimelineEventEntity
|
||||||
}
|
}
|
||||||
|
|
||||||
///**
|
|
||||||
// * Find all TimelineEventEntity that are threads bind to the Event with rootThreadEventId
|
|
||||||
// * @param rootThreadEventId The root eventId that will try to find bind threads
|
|
||||||
// */
|
|
||||||
//internal fun EventEntity.findAllThreadsForRootEventId(realm: Realm, rootThreadEventId: String): RealmResults<TimelineEventEntity> =
|
|
||||||
// TimelineEventEntity
|
|
||||||
// .whereRoomId(realm, roomId = roomId)
|
|
||||||
// .equalTo(TimelineEventEntityFields.ROOT.ROOT_THREAD_EVENT_ID, rootThreadEventId)
|
|
||||||
// .sort(TimelineEventEntityFields.DISPLAY_INDEX, Sort.DESCENDING)
|
|
||||||
// .findAll()
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Count the number of threads for the provided root thread eventId, and finds the latest event message
|
* Count the number of threads for the provided root thread eventId, and finds the latest event message
|
||||||
* @param rootThreadEventId The root eventId that will find the number of threads
|
* @param rootThreadEventId The root eventId that will find the number of threads
|
||||||
|
@ -124,7 +115,7 @@ internal fun EventEntity.threadSummaryInThread(realm: Realm, rootThreadEventId:
|
||||||
result = chunk.timelineEvents.sort(TimelineEventEntityFields.DISPLAY_INDEX, Sort.DESCENDING)?.firstOrNull {
|
result = chunk.timelineEvents.sort(TimelineEventEntityFields.DISPLAY_INDEX, Sort.DESCENDING)?.firstOrNull {
|
||||||
it.root?.rootThreadEventId == rootThreadEventId
|
it.root?.rootThreadEventId == rootThreadEventId
|
||||||
}
|
}
|
||||||
chunk = ChunkEntity.find(realm, roomId, nextToken = chunk.prevToken) ?: break
|
chunk = ChunkEntity.find(realm, roomId, nextToken = chunk.prevToken) ?: break
|
||||||
}
|
}
|
||||||
result ?: return null
|
result ?: return null
|
||||||
|
|
||||||
|
@ -139,7 +130,25 @@ internal fun TimelineEventEntity.Companion.findAllThreadsForRoomId(realm: Realm,
|
||||||
TimelineEventEntity
|
TimelineEventEntity
|
||||||
.whereRoomId(realm, roomId = roomId)
|
.whereRoomId(realm, roomId = roomId)
|
||||||
.equalTo(TimelineEventEntityFields.ROOT.IS_ROOT_THREAD, true)
|
.equalTo(TimelineEventEntityFields.ROOT.IS_ROOT_THREAD, true)
|
||||||
.sort("${TimelineEventEntityFields.ROOT.THREAD_SUMMARY_LATEST_MESSAGE}.${TimelineEventEntityFields.DISPLAY_INDEX}", Sort.DESCENDING)
|
.sort("${TimelineEventEntityFields.ROOT.THREAD_SUMMARY_LATEST_MESSAGE}.${TimelineEventEntityFields.ROOT.ORIGIN_SERVER_TS}", Sort.DESCENDING)
|
||||||
|
|
||||||
|
internal fun List<TimelineEvent>.mapEventsWithEdition(realm: Realm, roomId: String): List<TimelineEvent> =
|
||||||
|
this.map {
|
||||||
|
EventAnnotationsSummaryEntity
|
||||||
|
.where(realm, roomId, eventId = it.eventId)
|
||||||
|
.findFirst()
|
||||||
|
?.editSummary
|
||||||
|
?.editions
|
||||||
|
?.lastOrNull()
|
||||||
|
?.eventId
|
||||||
|
?.let { editedEventId ->
|
||||||
|
TimelineEventEntity.where(realm, roomId, eventId = editedEventId).findFirst()?.let { editedEvent ->
|
||||||
|
it.root.threadDetails = it.root.threadDetails?.copy(lastRootThreadEdition = editedEvent.root?.asDomain()?.getDecryptedTextSummary()
|
||||||
|
?: "(edited)")
|
||||||
|
it
|
||||||
|
} ?: it
|
||||||
|
} ?: it
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the number of all the local notifications for the specified room
|
* Find the number of all the local notifications for the specified room
|
||||||
|
|
|
@ -40,6 +40,7 @@ import org.matrix.android.sdk.api.session.room.model.relation.ReactionContent
|
||||||
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
|
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
|
||||||
import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
|
import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
|
||||||
import org.matrix.android.sdk.internal.crypto.verification.toState
|
import org.matrix.android.sdk.internal.crypto.verification.toState
|
||||||
|
import org.matrix.android.sdk.internal.database.helper.findRootThreadEvent
|
||||||
import org.matrix.android.sdk.internal.database.mapper.ContentMapper
|
import org.matrix.android.sdk.internal.database.mapper.ContentMapper
|
||||||
import org.matrix.android.sdk.internal.database.mapper.EventMapper
|
import org.matrix.android.sdk.internal.database.mapper.EventMapper
|
||||||
import org.matrix.android.sdk.internal.database.model.EditAggregatedSummaryEntity
|
import org.matrix.android.sdk.internal.database.model.EditAggregatedSummaryEntity
|
||||||
|
@ -64,7 +65,7 @@ import javax.inject.Inject
|
||||||
internal class EventRelationsAggregationProcessor @Inject constructor(
|
internal class EventRelationsAggregationProcessor @Inject constructor(
|
||||||
@UserId private val userId: String,
|
@UserId private val userId: String,
|
||||||
private val stateEventDataSource: StateEventDataSource
|
private val stateEventDataSource: StateEventDataSource
|
||||||
) : EventInsertLiveProcessor {
|
) : EventInsertLiveProcessor {
|
||||||
|
|
||||||
private val allowedTypes = listOf(
|
private val allowedTypes = listOf(
|
||||||
EventType.MESSAGE,
|
EventType.MESSAGE,
|
||||||
|
@ -302,6 +303,29 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!isLocalEcho) {
|
||||||
|
val replaceEvent = TimelineEventEntity.where(realm, roomId, eventId).findFirst()
|
||||||
|
handleThreadSummaryEdition(editedEvent, replaceEvent, existingSummary?.editions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the edition is on the latest thread event, and update it accordingly
|
||||||
|
*/
|
||||||
|
private fun handleThreadSummaryEdition(editedEvent: EventEntity?,
|
||||||
|
replaceEvent: TimelineEventEntity?,
|
||||||
|
editions: List<EditionOfEvent>?) {
|
||||||
|
replaceEvent ?: return
|
||||||
|
editedEvent ?: return
|
||||||
|
editedEvent.findRootThreadEvent()?.apply {
|
||||||
|
val threadSummaryEventId = threadSummaryLatestMessage?.eventId
|
||||||
|
if (editedEvent.eventId == threadSummaryEventId || editions?.any { it.eventId == threadSummaryEventId } == true) {
|
||||||
|
// The edition is for the latest event or for any event replaced, this is to handle multiple
|
||||||
|
// edits of the same latest event
|
||||||
|
threadSummaryLatestMessage = replaceEvent
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleResponse(realm: Realm,
|
private fun handleResponse(realm: Realm,
|
||||||
|
|
|
@ -37,6 +37,7 @@ import org.matrix.android.sdk.internal.database.RealmSessionProvider
|
||||||
import org.matrix.android.sdk.internal.database.helper.findAllLocalThreadNotificationsForRoomId
|
import org.matrix.android.sdk.internal.database.helper.findAllLocalThreadNotificationsForRoomId
|
||||||
import org.matrix.android.sdk.internal.database.helper.findAllThreadsForRoomId
|
import org.matrix.android.sdk.internal.database.helper.findAllThreadsForRoomId
|
||||||
import org.matrix.android.sdk.internal.database.helper.isUserParticipatingInThread
|
import org.matrix.android.sdk.internal.database.helper.isUserParticipatingInThread
|
||||||
|
import org.matrix.android.sdk.internal.database.helper.mapEventsWithEdition
|
||||||
import org.matrix.android.sdk.internal.database.lightweight.LightweightSettingsStorage
|
import org.matrix.android.sdk.internal.database.lightweight.LightweightSettingsStorage
|
||||||
import org.matrix.android.sdk.internal.database.mapper.TimelineEventMapper
|
import org.matrix.android.sdk.internal.database.mapper.TimelineEventMapper
|
||||||
import org.matrix.android.sdk.internal.database.model.EventEntity
|
import org.matrix.android.sdk.internal.database.model.EventEntity
|
||||||
|
@ -157,6 +158,12 @@ internal class DefaultTimelineService @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun mapEventsWithEdition(threads: List<TimelineEvent>): List<TimelineEvent> {
|
||||||
|
return Realm.getInstance(monarchy.realmConfiguration).use {
|
||||||
|
threads.mapEventsWithEdition(it, roomId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun markThreadAsRead(rootThreadEventId: String) {
|
override suspend fun markThreadAsRead(rootThreadEventId: String) {
|
||||||
monarchy.awaitTransaction {
|
monarchy.awaitTransaction {
|
||||||
EventEntity.where(
|
EventEntity.where(
|
||||||
|
|
|
@ -60,6 +60,7 @@ class ThreadListController @Inject constructor(
|
||||||
?.forEach { timelineEvent ->
|
?.forEach { timelineEvent ->
|
||||||
val date = dateFormatter.format(timelineEvent.root.threadDetails?.lastMessageTimestamp, DateFormatKind.ROOM_LIST)
|
val date = dateFormatter.format(timelineEvent.root.threadDetails?.lastMessageTimestamp, DateFormatKind.ROOM_LIST)
|
||||||
val decryptionErrorMessage = stringProvider.getString(R.string.encrypted_message)
|
val decryptionErrorMessage = stringProvider.getString(R.string.encrypted_message)
|
||||||
|
val lastRootThreadEdition = timelineEvent.root.threadDetails?.lastRootThreadEdition
|
||||||
threadListItem {
|
threadListItem {
|
||||||
id(timelineEvent.eventId)
|
id(timelineEvent.eventId)
|
||||||
avatarRenderer(host.avatarRenderer)
|
avatarRenderer(host.avatarRenderer)
|
||||||
|
@ -68,7 +69,7 @@ class ThreadListController @Inject constructor(
|
||||||
date(date)
|
date(date)
|
||||||
rootMessageDeleted(timelineEvent.root.isRedacted())
|
rootMessageDeleted(timelineEvent.root.isRedacted())
|
||||||
threadNotificationState(timelineEvent.root.threadDetails?.threadNotificationState ?: ThreadNotificationState.NO_NEW_MESSAGE)
|
threadNotificationState(timelineEvent.root.threadDetails?.threadNotificationState ?: ThreadNotificationState.NO_NEW_MESSAGE)
|
||||||
rootMessage(timelineEvent.root.getDecryptedTextSummary() ?: decryptionErrorMessage)
|
rootMessage(lastRootThreadEdition ?: timelineEvent.root.getDecryptedTextSummary() ?: decryptionErrorMessage)
|
||||||
lastMessage(timelineEvent.root.threadDetails?.threadSummaryLatestTextMessage ?: decryptionErrorMessage)
|
lastMessage(timelineEvent.root.threadDetails?.threadSummaryLatestTextMessage ?: decryptionErrorMessage)
|
||||||
lastMessageCounter(timelineEvent.root.threadDetails?.numberOfThreads.toString())
|
lastMessageCounter(timelineEvent.root.threadDetails?.numberOfThreads.toString())
|
||||||
lastMessageMatrixItem(timelineEvent.root.threadDetails?.threadSummarySenderInfo?.toMatrixItem())
|
lastMessageMatrixItem(timelineEvent.root.threadDetails?.threadSummarySenderInfo?.toMatrixItem())
|
||||||
|
|
|
@ -61,6 +61,7 @@ class ThreadListViewModel @AssistedInject constructor(@Assisted val initialState
|
||||||
private fun observeThreadsList() {
|
private fun observeThreadsList() {
|
||||||
room?.flow()
|
room?.flow()
|
||||||
?.liveThreadList()
|
?.liveThreadList()
|
||||||
|
?.map { room.mapEventsWithEdition(it) }
|
||||||
?.map {
|
?.map {
|
||||||
it.map { threadRootEvent ->
|
it.map { threadRootEvent ->
|
||||||
val isParticipating = room.isUserParticipatingInThread(threadRootEvent.eventId)
|
val isParticipating = room.isUserParticipatingInThread(threadRootEvent.eventId)
|
||||||
|
|
Loading…
Reference in New Issue