From 25ea1ba641e9bb182e171841d4f5f8371d681778 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 9 Mar 2021 13:55:56 +0100 Subject: [PATCH] Fix issue with local echo --- CHANGES.md | 1 + .../sdk/api/session/events/model/Event.kt | 13 ++++++++ .../session/room/send/LocalEchoRepository.kt | 4 +-- .../session/room/timeline/DefaultTimeline.kt | 30 ++++++++++--------- .../session/room/timeline/UIEchoManager.kt | 20 ++++++------- 5 files changed, 41 insertions(+), 27 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 8783bce6d8..7ab26c1d04 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -15,6 +15,7 @@ Improvements 🙌: Bugfix 🐛: - Try to fix crash about UrlPreview (#2640) - Be robust if Event.type is missing (#2946) + - Snappier message send status Translations 🗣: - All string resources and translations have been moved to the application module. Weblate project for the SDK will be removed. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt index a208b15364..844e8dbbab 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt @@ -98,6 +98,19 @@ data class Event( @Transient 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. * @return true if event is state event. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoRepository.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoRepository.kt index 1277e81fe3..70245cbd5e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoRepository.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoRepository.kt @@ -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.internal.database.RealmSessionProvider 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.asDomain 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.util.awaitTransaction import timber.log.Timber +import java.util.UUID import javax.inject.Inject 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 roomMemberHelper = RoomMemberHelper(realm, roomId) val myUser = roomMemberHelper.getLastRoomMember(senderId) - val localId = TimelineEventEntity.nextId(realm) + val localId = UUID.randomUUID().mostSignificantBits TimelineEventEntity(localId).also { it.root = eventEntity it.eventId = event.eventId diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt index 33ce93b5a8..d0946abe28 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt @@ -418,27 +418,29 @@ internal class DefaultTimeline( } private fun buildSendingEvents(): List { - val builtSendingEvents = ArrayList() + val builtSendingEvents = mutableListOf() if (hasReachedEnd(Timeline.Direction.FORWARDS) && !hasMoreInCache(Timeline.Direction.FORWARDS)) { - builtSendingEvents.addAll( - uiEchoManager.getInMemorySendingEvents() - .filterEventsWithSettings(settings) - ) - + uiEchoManager.getInMemorySendingEvents() + .filterSendingEventsTo(builtSendingEvents) sendingEvents - .map { timelineEventMapper.map(it) } - // Filter out sending event that are not displayable! - .filterEventsWithSettings(settings) - .forEach { timelineEvent -> - if (builtSendingEvents.find { it.eventId == timelineEvent.eventId } == null) { - uiEchoManager.updateSentStateWithUiEcho(timelineEvent) - builtSendingEvents.add(timelineEvent) - } + .filter { timelineEvent -> + builtSendingEvents.none { it.eventId == timelineEvent.eventId } } + .map { timelineEventMapper.map(it) } + .filterSendingEventsTo(builtSendingEvents) } return builtSendingEvents } + private fun List.filterSendingEventsTo(target: MutableList) { + 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 { return isReady.get() && !getState(direction).isPaginating && hasMoreToLoad(direction) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/UIEchoManager.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/UIEchoManager.kt index fdafdb161d..67d0d90d77 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/UIEchoManager.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/UIEchoManager.kt @@ -55,10 +55,6 @@ internal class UIEchoManager( eventIds.forEach { eventId -> inMemorySendingEvents.removeAll { eventId == it.eventId } } - inMemorySendingStates.keys.removeAll { key -> - eventIds.find { it == key } == null - } - inMemoryReactions.forEach { (_, uiEchoData) -> uiEchoData.removeAll { data -> // 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) { - inMemorySendingStates[element.eventId]?.let { - // Timber.v("## ${System.currentTimeMillis()} Send event refresh echo with live state ${it} from state ${element.root.sendState}") - if(element.root.sendState != SendState.SENT) { - element.root.sendState = it - } - } + fun updateSentStateWithUiEcho(timelineEvent: TimelineEvent): TimelineEvent { + if (timelineEvent.root.sendState.isSent()) return timelineEvent + val inMemoryState = inMemorySendingStates[timelineEvent.eventId] ?: return timelineEvent + // Timber.v("## ${System.currentTimeMillis()} Send event refresh echo with live state $inMemoryState from state ${element.root.sendState}") + return timelineEvent.copy( + root = timelineEvent.root.copyAll() + .also { it.sendState = inMemoryState } + ) } fun onSyncedEvent(transactionId: String?) { @@ -177,5 +174,6 @@ internal class UIEchoManager( inMemoryReactions.forEach { (_, u) -> u.filterNot { it.localEchoId == transactionId } } + inMemorySendingStates.remove(transactionId) } }