diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt index 843c232890..203fb30794 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt @@ -35,7 +35,13 @@ data class MessageContent( sealed interface InReplyTo { + /** The event details are not loaded yet. We can fetch them. */ data class NotLoaded(val eventId: EventId) : InReplyTo + + /** The event details are pending to be fetched. We should **not** fetch them again. */ + object Pending : InReplyTo + + /** The event details are available. */ data class Ready( val eventId: EventId, val content: MessageContent, @@ -44,6 +50,14 @@ sealed interface InReplyTo { val senderAvatarUrl: String?, ) : InReplyTo + /** + * Fetching the event details failed. + * + * We can try to fetch them again **with a proper retry strategy**, but not blindly: + * + * If the reason for the failure is consistent on the server, we'd enter a loop + * where we keep trying to fetch the same event. + * */ object Error : InReplyTo } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventTimelineItem.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventTimelineItem.kt index ef7f4d47b0..50bf5f8ce5 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventTimelineItem.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventTimelineItem.kt @@ -42,6 +42,6 @@ data class EventTimelineItem( } fun hasNotLoadedInReplyTo(): Boolean { val details = inReplyTo() - return details is InReplyTo.NotLoaded || details is InReplyTo.Error + return details is InReplyTo.NotLoaded } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt index 8b1df21845..16434aa3c8 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt @@ -41,6 +41,7 @@ import io.element.android.libraries.matrix.impl.media.map import io.element.android.libraries.matrix.impl.room.location.toInner import io.element.android.libraries.matrix.impl.timeline.RustMatrixTimeline import io.element.android.libraries.matrix.impl.timeline.backPaginationStatusFlow +import io.element.android.libraries.matrix.impl.timeline.eventOrigin import io.element.android.libraries.matrix.impl.timeline.timelineDiffFlow import io.element.android.libraries.sessionstorage.api.SessionData import io.element.android.services.toolbox.api.systemclock.SystemClock @@ -54,6 +55,7 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import org.matrix.rustcomponents.sdk.EventItemOrigin import org.matrix.rustcomponents.sdk.RequiredState import org.matrix.rustcomponents.sdk.Room import org.matrix.rustcomponents.sdk.RoomListItem @@ -119,9 +121,11 @@ class RustMatrixRoom( roomCoroutineScope.launch(roomDispatcher) { innerRoom.timelineDiffFlow { initialList -> _timeline.postItems(initialList) - }.onEach { - _syncUpdateFlow.value = systemClock.epochMillis() - _timeline.postDiff(it) + }.onEach { diff -> + if (diff.eventOrigin() == EventItemOrigin.SYNC) { + _syncUpdateFlow.value = systemClock.epochMillis() + } + _timeline.postDiff(diff) }.launchIn(this) innerRoom.backPaginationStatusFlow() diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/TimelineDiffExt.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/TimelineDiffExt.kt new file mode 100644 index 0000000000..59165463bb --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/TimelineDiffExt.kt @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.matrix.impl.timeline + +import org.matrix.rustcomponents.sdk.EventItemOrigin +import org.matrix.rustcomponents.sdk.TimelineChange +import org.matrix.rustcomponents.sdk.TimelineDiff +import org.matrix.rustcomponents.sdk.TimelineItem + +/** + * Tries to get an event origin from the TimelineDiff. + * If there is multiple events in the diff, uses the first one as it should be a good indicator. + */ +internal fun TimelineDiff.eventOrigin(): EventItemOrigin? { + return when (change()) { + TimelineChange.APPEND -> { + append()?.firstOrNull()?.eventOrigin() + } + TimelineChange.PUSH_BACK -> { + pushBack()?.eventOrigin() + } + TimelineChange.PUSH_FRONT -> { + pushFront()?.eventOrigin() + } + TimelineChange.SET -> { + set()?.item?.eventOrigin() + } + TimelineChange.INSERT -> { + insert()?.item?.eventOrigin() + } + TimelineChange.RESET -> { + reset()?.firstOrNull()?.eventOrigin() + } + else -> null + } +} + +private fun TimelineItem.eventOrigin(): EventItemOrigin? { + return asEvent()?.origin() +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt index 2ecad95e4f..9f4df1f6b3 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt @@ -58,7 +58,8 @@ class EventMessageMapper { ) } is RepliedToEventDetails.Error -> InReplyTo.Error - is RepliedToEventDetails.Pending, is RepliedToEventDetails.Unavailable -> InReplyTo.NotLoaded(inReplyToId!!) + is RepliedToEventDetails.Pending -> InReplyTo.Pending + is RepliedToEventDetails.Unavailable -> InReplyTo.NotLoaded(inReplyToId!!) } } MessageContent(