Timeline/Read: update read receipt locally to

This commit is contained in:
ganfra 2019-09-19 16:17:58 +02:00
parent 88fb9667a3
commit 9668487b6b
7 changed files with 50 additions and 53 deletions

View File

@ -32,7 +32,6 @@ import im.vector.matrix.android.internal.database.model.ChunkEntity
import im.vector.matrix.android.internal.database.model.ReadMarkerEntity
import im.vector.matrix.android.internal.database.model.ReadReceiptEntity
import im.vector.matrix.android.internal.database.model.ReadReceiptsSummaryEntity
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
import im.vector.matrix.android.internal.database.query.find
import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom
import im.vector.matrix.android.internal.database.query.where

View File

@ -30,6 +30,7 @@ import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.session.room.RoomAPI
import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory
import im.vector.matrix.android.internal.session.sync.ReadReceiptHandler
import im.vector.matrix.android.internal.session.sync.RoomFullyReadHandler
import im.vector.matrix.android.internal.task.Task
import im.vector.matrix.android.internal.util.awaitTransaction
@ -53,7 +54,8 @@ private const val READ_RECEIPT = "m.read"
internal class DefaultSetReadMarkersTask @Inject constructor(private val roomAPI: RoomAPI,
private val credentials: Credentials,
private val monarchy: Monarchy,
private val roomFullyReadHandler: RoomFullyReadHandler
private val roomFullyReadHandler: RoomFullyReadHandler,
private val readReceiptHandler: ReadReceiptHandler
) : SetReadMarkersTask {
override suspend fun execute(params: SetReadMarkersTask.Params) {
@ -102,11 +104,12 @@ internal class DefaultSetReadMarkersTask @Inject constructor(private val roomAPI
monarchy.awaitTransaction { realm ->
val readMarkerId = markers[READ_MARKER]
val readReceiptId = markers[READ_RECEIPT]
if (readMarkerId != null) {
roomFullyReadHandler.handle(realm, roomId, FullyReadContent(readMarkerId))
}
if (readReceiptId != null) {
val readReceiptContent = ReadReceiptHandler.createContent(credentials.userId, readReceiptId)
readReceiptHandler.handle(realm, roomId, readReceiptContent, false)
val isLatestReceived = TimelineEventEntity.latestEvent(realm, roomId = roomId, includesSending = false)?.eventId == readReceiptId
if (isLatestReceived) {
val roomSummary = RoomSummaryEntity.where(realm, roomId).findFirst()
@ -118,7 +121,6 @@ internal class DefaultSetReadMarkersTask @Inject constructor(private val roomAPI
}
}
private fun isReadMarkerMoreRecent(roomId: String, fullyReadEventId: String): Boolean {
return Realm.getInstance(monarchy.realmConfiguration).use { realm ->
val readMarkerEntity = ReadMarkerEntity.where(realm, roomId = roomId).findFirst()
@ -130,7 +132,6 @@ internal class DefaultSetReadMarkersTask @Inject constructor(private val roomAPI
}
}
private fun isEventRead(roomId: String, eventId: String): Boolean {
return Realm.getInstance(monarchy.realmConfiguration).use { realm ->
val readReceipt = ReadReceiptEntity.where(realm, roomId, credentials.userId).findFirst()
@ -145,21 +146,5 @@ internal class DefaultSetReadMarkersTask @Inject constructor(private val roomAPI
}
}
private fun SetReadMarkersTask.Params.fullyReadEventId(): String? {
if (fullyReadEventId != null) {
return this.fullyReadEventId
} else {
Realm.getInstance(monarchy.realmConfiguration).use { realm ->
val readReceipt = ReadReceiptEntity.where(realm, roomId, credentials.userId).findFirst()
val readMarker = ReadMarkerEntity.where(realm, roomId).findFirst()
return if (readMarker?.eventId == readReceipt?.eventId) {
readReceiptEventId
} else {
null
}
}
}
}
}

View File

@ -38,6 +38,22 @@ private const val TIMESTAMP_KEY = "ts"
internal class ReadReceiptHandler @Inject constructor() {
companion object {
fun createContent(userId: String, eventId: String): ReadReceiptContent {
return mapOf(
eventId to mapOf(
READ_KEY to mapOf(
userId to mapOf(
TIMESTAMP_KEY to System.currentTimeMillis().toDouble()
)
)
)
)
}
}
fun handle(realm: Realm, roomId: String, content: ReadReceiptContent?, isInitialSync: Boolean) {
if (content == null) {
return

View File

@ -26,6 +26,7 @@ import android.view.animation.AnimationUtils
import androidx.core.view.isInvisible
import im.vector.riotx.R
import kotlinx.coroutines.*
import timber.log.Timber
private const val DELAY_IN_MS = 1_500L
@ -44,6 +45,7 @@ class ReadMarkerView @JvmOverloads constructor(
private var callbackDispatcherJob: Job? = null
fun bindView(eventId: String?, hasReadMarker: Boolean, displayReadMarker: Boolean, readMarkerCallback: Callback) {
Timber.v("Bind event $eventId - hasReadMarker: $hasReadMarker - displayReadMarker: $displayReadMarker")
this.eventId = eventId
this.callback = readMarkerCallback
if (displayReadMarker) {

View File

@ -687,7 +687,7 @@ class RoomDetailFragment :
val summary = state.asyncRoomSummary()
val inviter = state.asyncInviter()
if (summary?.membership == Membership.JOIN) {
timelineEventController.update(state.timeline, state.highlightedEventId, state.hideReadMarker)
timelineEventController.update(state)
inviteView.visibility = View.GONE
val uid = session.myUserId
val meMember = session.getRoom(state.roomId)?.getRoomMember(uid)

View File

@ -694,8 +694,6 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
readMarker.getOrNull() == readReceipt.getOrNull()
}
)
.throttleLast(250, TimeUnit.MILLISECONDS)
.distinctUntilChanged()
.startWith(false)
.subscribe {
setState { copy(hideReadMarker = it) }

View File

@ -31,6 +31,7 @@ import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.riotx.core.date.VectorDateFormatter
import im.vector.riotx.core.epoxy.LoadingItem_
import im.vector.riotx.core.extensions.localDateTime
import im.vector.riotx.features.home.room.detail.RoomDetailViewState
import im.vector.riotx.features.home.room.detail.timeline.factory.MergedHeaderItemFactory
import im.vector.riotx.features.home.room.detail.timeline.factory.TimelineItemFactory
import im.vector.riotx.features.home.room.detail.timeline.helper.*
@ -140,11 +141,10 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
requestModelBuild()
}
fun update(timeline: Timeline?, eventIdToHighlight: String?, hideReadMarker: Boolean) {
if (this.timeline != timeline) {
this.timeline = timeline
this.timeline?.listener = this
fun update(viewState: RoomDetailViewState) {
if (timeline != viewState.timeline) {
timeline = viewState.timeline
timeline?.listener = this
// Clear cache
synchronized(modelCache) {
for (i in 0 until modelCache.size) {
@ -152,23 +152,22 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
}
}
}
var requestModelBuild = false
if (this.eventIdToHighlight != eventIdToHighlight) {
if (eventIdToHighlight != viewState.highlightedEventId) {
// Clear cache to force a refresh
synchronized(modelCache) {
for (i in 0 until modelCache.size) {
if (modelCache[i]?.eventId == eventIdToHighlight
|| modelCache[i]?.eventId == this.eventIdToHighlight) {
if (modelCache[i]?.eventId == viewState.highlightedEventId
|| modelCache[i]?.eventId == eventIdToHighlight) {
modelCache[i] = null
}
}
}
this.eventIdToHighlight = eventIdToHighlight
eventIdToHighlight = viewState.highlightedEventId
requestModelBuild = true
}
if (this.hideReadMarker != hideReadMarker) {
this.hideReadMarker = hideReadMarker
if (hideReadMarker != viewState.hideReadMarker) {
hideReadMarker = viewState.hideReadMarker
requestModelBuild = true
}
if (requestModelBuild) {
@ -256,7 +255,6 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
val date = event.root.localDateTime()
val nextDate = nextEvent?.root?.localDateTime()
val addDaySeparator = date.toLocalDate() != nextDate?.toLocalDate()
val eventModel = timelineItemFactory.create(event, nextEvent, eventIdToHighlight, hideReadMarker, callback).also {
it.id(event.localId)
it.setOnVisibilityStateChanged(TimelineEventVisibilityStateChangedListener(callback, event))
@ -298,7 +296,6 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
}
}
fun searchPositionOfEvent(eventId: String): Int? = synchronized(modelCache) {
// Search in the cache
var realPosition = 0