Fix issue with local echo

This commit is contained in:
Benoit Marty 2021-03-09 13:55:56 +01:00
parent 0cd728222e
commit 25ea1ba641
5 changed files with 41 additions and 27 deletions

View File

@ -15,6 +15,7 @@ Improvements 🙌:
Bugfix 🐛: Bugfix 🐛:
- Try to fix crash about UrlPreview (#2640) - Try to fix crash about UrlPreview (#2640)
- Be robust if Event.type is missing (#2946) - Be robust if Event.type is missing (#2946)
- Snappier message send status
Translations 🗣: Translations 🗣:
- All string resources and translations have been moved to the application module. Weblate project for the SDK will be removed. - All string resources and translations have been moved to the application module. Weblate project for the SDK will be removed.

View File

@ -98,6 +98,19 @@ data class Event(
@Transient @Transient
var ageLocalTs: Long? = null var ageLocalTs: Long? = null
/**
* Copy all fields, including transient fields
*/
fun copyAll(): Event {
return copy().also {
it.mxDecryptionResult = mxDecryptionResult
it.mCryptoError = mCryptoError
it.mCryptoErrorReason = mCryptoErrorReason
it.sendState = sendState
it.ageLocalTs = ageLocalTs
}
}
/** /**
* Check if event is a state event. * Check if event is a state event.
* @return true if event is state event. * @return true if event is state event.

View File

@ -27,7 +27,6 @@ import org.matrix.android.sdk.api.session.room.send.SendState
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
import org.matrix.android.sdk.internal.database.RealmSessionProvider import org.matrix.android.sdk.internal.database.RealmSessionProvider
import org.matrix.android.sdk.internal.database.asyncTransaction import org.matrix.android.sdk.internal.database.asyncTransaction
import org.matrix.android.sdk.internal.database.helper.nextId
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.mapper.asDomain 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.mapper.toEntity
@ -45,6 +44,7 @@ import org.matrix.android.sdk.internal.session.room.timeline.TimelineInput
import org.matrix.android.sdk.internal.task.TaskExecutor import org.matrix.android.sdk.internal.task.TaskExecutor
import org.matrix.android.sdk.internal.util.awaitTransaction import org.matrix.android.sdk.internal.util.awaitTransaction
import timber.log.Timber import timber.log.Timber
import java.util.UUID
import javax.inject.Inject import javax.inject.Inject
internal class LocalEchoRepository @Inject constructor(@SessionDatabase private val monarchy: Monarchy, internal class LocalEchoRepository @Inject constructor(@SessionDatabase private val monarchy: Monarchy,
@ -64,7 +64,7 @@ internal class LocalEchoRepository @Inject constructor(@SessionDatabase private
val eventEntity = event.toEntity(roomId, SendState.UNSENT, System.currentTimeMillis()) val eventEntity = event.toEntity(roomId, SendState.UNSENT, System.currentTimeMillis())
val roomMemberHelper = RoomMemberHelper(realm, roomId) val roomMemberHelper = RoomMemberHelper(realm, roomId)
val myUser = roomMemberHelper.getLastRoomMember(senderId) val myUser = roomMemberHelper.getLastRoomMember(senderId)
val localId = TimelineEventEntity.nextId(realm) val localId = UUID.randomUUID().mostSignificantBits
TimelineEventEntity(localId).also { TimelineEventEntity(localId).also {
it.root = eventEntity it.root = eventEntity
it.eventId = event.eventId it.eventId = event.eventId

View File

@ -418,27 +418,29 @@ internal class DefaultTimeline(
} }
private fun buildSendingEvents(): List<TimelineEvent> { private fun buildSendingEvents(): List<TimelineEvent> {
val builtSendingEvents = ArrayList<TimelineEvent>() val builtSendingEvents = mutableListOf<TimelineEvent>()
if (hasReachedEnd(Timeline.Direction.FORWARDS) && !hasMoreInCache(Timeline.Direction.FORWARDS)) { if (hasReachedEnd(Timeline.Direction.FORWARDS) && !hasMoreInCache(Timeline.Direction.FORWARDS)) {
builtSendingEvents.addAll( uiEchoManager.getInMemorySendingEvents()
uiEchoManager.getInMemorySendingEvents() .filterSendingEventsTo(builtSendingEvents)
.filterEventsWithSettings(settings)
)
sendingEvents sendingEvents
.map { timelineEventMapper.map(it) } .filter { timelineEvent ->
// Filter out sending event that are not displayable! builtSendingEvents.none { it.eventId == timelineEvent.eventId }
.filterEventsWithSettings(settings)
.forEach { timelineEvent ->
if (builtSendingEvents.find { it.eventId == timelineEvent.eventId } == null) {
uiEchoManager.updateSentStateWithUiEcho(timelineEvent)
builtSendingEvents.add(timelineEvent)
}
} }
.map { timelineEventMapper.map(it) }
.filterSendingEventsTo(builtSendingEvents)
} }
return builtSendingEvents return builtSendingEvents
} }
private fun List<TimelineEvent>.filterSendingEventsTo(target: MutableList<TimelineEvent>) {
target.addAll(
// Filter out sending event that are not displayable!
filterEventsWithSettings(settings)
// Get most up to date send state (in memory)
.map { uiEchoManager.updateSentStateWithUiEcho(it) }
)
}
private fun canPaginate(direction: Timeline.Direction): Boolean { private fun canPaginate(direction: Timeline.Direction): Boolean {
return isReady.get() && !getState(direction).isPaginating && hasMoreToLoad(direction) return isReady.get() && !getState(direction).isPaginating && hasMoreToLoad(direction)
} }

View File

@ -55,10 +55,6 @@ internal class UIEchoManager(
eventIds.forEach { eventId -> eventIds.forEach { eventId ->
inMemorySendingEvents.removeAll { eventId == it.eventId } inMemorySendingEvents.removeAll { eventId == it.eventId }
} }
inMemorySendingStates.keys.removeAll { key ->
eventIds.find { it == key } == null
}
inMemoryReactions.forEach { (_, uiEchoData) -> inMemoryReactions.forEach { (_, uiEchoData) ->
uiEchoData.removeAll { data -> uiEchoData.removeAll { data ->
// I remove the uiEcho, when the related event is not anymore in the sending list // I remove the uiEcho, when the related event is not anymore in the sending list
@ -159,13 +155,14 @@ internal class UIEchoManager(
) )
} }
fun updateSentStateWithUiEcho(element: TimelineEvent) { fun updateSentStateWithUiEcho(timelineEvent: TimelineEvent): TimelineEvent {
inMemorySendingStates[element.eventId]?.let { if (timelineEvent.root.sendState.isSent()) return timelineEvent
// Timber.v("## ${System.currentTimeMillis()} Send event refresh echo with live state ${it} from state ${element.root.sendState}") val inMemoryState = inMemorySendingStates[timelineEvent.eventId] ?: return timelineEvent
if(element.root.sendState != SendState.SENT) { // Timber.v("## ${System.currentTimeMillis()} Send event refresh echo with live state $inMemoryState from state ${element.root.sendState}")
element.root.sendState = it return timelineEvent.copy(
} root = timelineEvent.root.copyAll()
} .also { it.sendState = inMemoryState }
)
} }
fun onSyncedEvent(transactionId: String?) { fun onSyncedEvent(transactionId: String?) {
@ -177,5 +174,6 @@ internal class UIEchoManager(
inMemoryReactions.forEach { (_, u) -> inMemoryReactions.forEach { (_, u) ->
u.filterNot { it.localEchoId == transactionId } u.filterNot { it.localEchoId == transactionId }
} }
inMemorySendingStates.remove(transactionId)
} }
} }