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 217aaf10e9..23b3912836 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 @@ -226,7 +226,15 @@ internal class DefaultTimeline(private val roomId: String, updateState(direction) { it.copy(loading = true) } - val loadMoreResult = strategy.loadMore(count, direction, fetchOnServerIfNeeded) + val loadMoreResult = try { + strategy.loadMore(count, direction, fetchOnServerIfNeeded) + } catch (throwable: Throwable) { + // Timeline could not be loaded with a (likely) permanent issue, such as the + // server now knowing the initialEventId, so we want to show an error message + // and possibly restart without initialEventId. + onTimelineFailure(throwable) + return false + } Timber.v("$baseLogMessage: result $loadMoreResult") val hasMoreToLoad = loadMoreResult != LoadMoreResult.REACHED_END updateState(direction) { @@ -363,6 +371,14 @@ internal class DefaultTimeline(private val roomId: String, } } + private fun onTimelineFailure(throwable: Throwable) { + timelineScope.launch(coroutineDispatchers.main) { + listeners.forEach { + tryOrNull { it.onTimelineFailure(throwable) } + } + } + } + private fun buildStrategy(mode: LoadTimelineStrategy.Mode): LoadTimelineStrategy { return LoadTimelineStrategy( roomId = roomId, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LoadTimelineStrategy.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LoadTimelineStrategy.kt index 4526756ce5..a40f9bbf1c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LoadTimelineStrategy.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LoadTimelineStrategy.kt @@ -22,6 +22,8 @@ import io.realm.Realm import io.realm.RealmResults import kotlinx.coroutines.CompletableDeferred import org.matrix.android.sdk.api.extensions.orFalse +import org.matrix.android.sdk.api.failure.Failure +import org.matrix.android.sdk.api.failure.MatrixError import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.api.session.room.timeline.Timeline import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent @@ -183,6 +185,10 @@ internal class LoadTimelineStrategy( getContextLatch?.await() getContextLatch = null } catch (failure: Throwable) { + if (failure is Failure.ServerError && failure.error.code == MatrixError.M_NOT_FOUND) { + // This failure is likely permanent, so handle in DefaultTimeline to restart without eventId + throw failure + } return LoadMoreResult.FAILURE } }