From b2cdeb87f49ab3fcac4b2655b412a9d917c9f4ea Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 14 Jan 2019 16:18:39 +0100 Subject: [PATCH] Fix some timeline issues and rename EnrichedEvent to TimelineEvent as it's only used in this context. --- .../riotredesign/core/extensions/LiveData.kt | 4 +- .../riotredesign/core/utils/Constants.kt | 6 -- .../core/utils/FragmentArgumentDelegate.kt | 63 ------------------- .../core/utils/{Event.kt => LiveEvent.kt} | 10 +-- .../features/home/HomeActivityViewModel.kt | 10 +-- .../features/home/HomeNavigator.kt | 2 +- .../home/room/detail/RoomDetailFragment.kt | 11 +++- .../room/detail/ScrollOnNewMessageCallback.kt | 4 +- .../detail/timeline/MessageItemFactory.kt | 6 +- .../room/detail/timeline/TextItemFactory.kt | 4 +- .../timeline/TimelineEventController.kt | 6 +- .../paging/PagedListEpoxyController.kt | 4 ++ .../room/timeline/TimelineHolderTest.kt | 16 ++--- .../matrix/android/api/session/Session.kt | 8 +++ .../interceptor/EnrichedEventInterceptor.kt | 12 ---- .../interceptor/TimelineEventInterceptor.kt | 12 ++++ .../{EnrichedEvent.kt => TimelineEvent.kt} | 2 +- .../api/session/room/timeline/TimelineData.kt | 4 +- .../database/RealmLiveEntityObserver.kt | 11 ++-- .../database/query/EventEntityQueries.kt | 5 +- .../internal/session/DefaultSession.kt | 9 +++ .../internal/session/SessionListeners.kt | 17 +++++ .../android/internal/session/SessionModule.kt | 5 +- .../session/group/GetGroupDataWorker.kt | 7 +-- .../session/group/GroupSummaryUpdater.kt | 13 ++-- .../internal/session/room/DefaultRoom.kt | 6 +- .../session/room/RoomSummaryUpdater.kt | 10 +-- .../session/room/prune/EventsPruner.kt | 7 +-- .../session/room/prune/PruneEventWorker.kt | 12 ++-- .../room/timeline/DefaultTimelineService.kt | 55 ++++++++++------ .../room/timeline/TimelineBoundaryCallback.kt | 8 +-- 31 files changed, 170 insertions(+), 179 deletions(-) delete mode 100644 app/src/main/java/im/vector/riotredesign/core/utils/Constants.kt delete mode 100644 app/src/main/java/im/vector/riotredesign/core/utils/FragmentArgumentDelegate.kt rename app/src/main/java/im/vector/riotredesign/core/utils/{Event.kt => LiveEvent.kt} (68%) delete mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/interceptor/EnrichedEventInterceptor.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/interceptor/TimelineEventInterceptor.kt rename matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/{EnrichedEvent.kt => TimelineEvent.kt} (95%) create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionListeners.kt diff --git a/app/src/main/java/im/vector/riotredesign/core/extensions/LiveData.kt b/app/src/main/java/im/vector/riotredesign/core/extensions/LiveData.kt index bf33acda..b012483a 100644 --- a/app/src/main/java/im/vector/riotredesign/core/extensions/LiveData.kt +++ b/app/src/main/java/im/vector/riotredesign/core/extensions/LiveData.kt @@ -3,7 +3,7 @@ package im.vector.riotredesign.core.extensions import android.arch.lifecycle.LifecycleOwner import android.arch.lifecycle.LiveData import android.arch.lifecycle.Observer -import im.vector.riotredesign.core.utils.Event +import im.vector.riotredesign.core.utils.LiveEvent import im.vector.riotredesign.core.utils.EventObserver inline fun LiveData.observeK(owner: LifecycleOwner, crossinline observer: (T?) -> Unit) { @@ -14,6 +14,6 @@ inline fun LiveData.observeNotNull(owner: LifecycleOwner, crossinline obs this.observe(owner, Observer { it?.run(observer) }) } -inline fun LiveData>.observeEvent(owner: LifecycleOwner, crossinline observer: (T) -> Unit) { +inline fun LiveData>.observeEvent(owner: LifecycleOwner, crossinline observer: (T) -> Unit) { this.observe(owner, EventObserver { it.run(observer) }) } \ No newline at end of file diff --git a/app/src/main/java/im/vector/riotredesign/core/utils/Constants.kt b/app/src/main/java/im/vector/riotredesign/core/utils/Constants.kt deleted file mode 100644 index ade18b36..00000000 --- a/app/src/main/java/im/vector/riotredesign/core/utils/Constants.kt +++ /dev/null @@ -1,6 +0,0 @@ -package im.vector.riotredesign.core.utils - -object Constants { - - -} \ No newline at end of file diff --git a/app/src/main/java/im/vector/riotredesign/core/utils/FragmentArgumentDelegate.kt b/app/src/main/java/im/vector/riotredesign/core/utils/FragmentArgumentDelegate.kt deleted file mode 100644 index 565c421c..00000000 --- a/app/src/main/java/im/vector/riotredesign/core/utils/FragmentArgumentDelegate.kt +++ /dev/null @@ -1,63 +0,0 @@ -package im.vector.riotredesign.core.utils - -import android.os.Binder -import android.os.Bundle -import android.support.v4.app.BundleCompat -import android.support.v4.app.Fragment -import kotlin.reflect.KProperty - -class FragmentArgumentDelegate : kotlin.properties.ReadWriteProperty { - - var value: T? = null - - override operator fun getValue(thisRef: android.support.v4.app.Fragment, property: kotlin.reflect.KProperty<*>): T? { - if (value == null) { - val args = thisRef.arguments - @Suppress("UNCHECKED_CAST") - value = args?.get(property.name) as T? - } - return value - } - - override operator fun setValue(thisRef: Fragment, property: KProperty<*>, value: T?) { - if (value == null) return - - if (thisRef.arguments == null) { - thisRef.arguments = Bundle() - } - val args = thisRef.arguments!! - val key = property.name - - when (value) { - is String -> args.putString(key, value) - is Int -> args.putInt(key, value) - is Short -> args.putShort(key, value) - is Long -> args.putLong(key, value) - is Byte -> args.putByte(key, value) - is ByteArray -> args.putByteArray(key, value) - is Char -> args.putChar(key, value) - is CharArray -> args.putCharArray(key, value) - is CharSequence -> args.putCharSequence(key, value) - is Float -> args.putFloat(key, value) - is Bundle -> args.putBundle(key, value) - is Binder -> BundleCompat.putBinder(args, key, value) - is android.os.Parcelable -> args.putParcelable(key, value) - is java.io.Serializable -> args.putSerializable(key, value) - else -> throw IllegalStateException("Type ${value.javaClass.name} of property ${property.name} is not supported") - } - } -} - -class UnsafeFragmentArgumentDelegate : kotlin.properties.ReadWriteProperty { - - private val innerDelegate = FragmentArgumentDelegate() - - override fun setValue(thisRef: Fragment, property: KProperty<*>, value: T) { - innerDelegate.setValue(thisRef, property, value) - } - - override fun getValue(thisRef: Fragment, property: KProperty<*>): T { - return innerDelegate.getValue(thisRef, property)!! - } - -} \ No newline at end of file diff --git a/app/src/main/java/im/vector/riotredesign/core/utils/Event.kt b/app/src/main/java/im/vector/riotredesign/core/utils/LiveEvent.kt similarity index 68% rename from app/src/main/java/im/vector/riotredesign/core/utils/Event.kt rename to app/src/main/java/im/vector/riotredesign/core/utils/LiveEvent.kt index d67ada66..153e3c3f 100644 --- a/app/src/main/java/im/vector/riotredesign/core/utils/Event.kt +++ b/app/src/main/java/im/vector/riotredesign/core/utils/LiveEvent.kt @@ -2,7 +2,7 @@ package im.vector.riotredesign.core.utils import android.arch.lifecycle.Observer -open class Event(private val content: T) { +open class LiveEvent(private val content: T) { var hasBeenHandled = false private set // Allow external read but not write @@ -26,13 +26,13 @@ open class Event(private val content: T) { } /** - * An [Observer] for [Event]s, simplifying the pattern of checking if the [Event]'s content has + * An [Observer] for [LiveEvent]s, simplifying the pattern of checking if the [LiveEvent]'s content has * already been handled. * - * [onEventUnhandledContent] is *only* called if the [Event]'s contents has not been handled. + * [onEventUnhandledContent] is *only* called if the [LiveEvent]'s contents has not been handled. */ -class EventObserver(private val onEventUnhandledContent: (T) -> Unit) : Observer> { - override fun onChanged(event: Event?) { +class EventObserver(private val onEventUnhandledContent: (T) -> Unit) : Observer> { + override fun onChanged(event: LiveEvent?) { event?.getContentIfNotHandled()?.let { value -> onEventUnhandledContent(value) } diff --git a/app/src/main/java/im/vector/riotredesign/features/home/HomeActivityViewModel.kt b/app/src/main/java/im/vector/riotredesign/features/home/HomeActivityViewModel.kt index 108469da..06509e8a 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/HomeActivityViewModel.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/HomeActivityViewModel.kt @@ -9,7 +9,7 @@ import im.vector.matrix.android.api.Matrix import im.vector.matrix.android.api.session.Session import im.vector.matrix.rx.rx import im.vector.riotredesign.core.platform.RiotViewModel -import im.vector.riotredesign.core.utils.Event +import im.vector.riotredesign.core.utils.LiveEvent import im.vector.riotredesign.features.home.room.list.RoomSelectionRepository import io.reactivex.rxkotlin.subscribeBy import org.koin.android.ext.android.get @@ -31,8 +31,8 @@ class HomeActivityViewModel(state: EmptyState, } } - private val _openRoomLiveData = MutableLiveData>() - val openRoomLiveData: LiveData> + private val _openRoomLiveData = MutableLiveData>() + val openRoomLiveData: LiveData> get() = _openRoomLiveData init { @@ -40,7 +40,7 @@ class HomeActivityViewModel(state: EmptyState, if (lastSelectedRoom == null) { getTheFirstRoomWhenAvailable() } else { - _openRoomLiveData.postValue(Event(lastSelectedRoom)) + _openRoomLiveData.postValue(LiveEvent(lastSelectedRoom)) } } @@ -51,7 +51,7 @@ class HomeActivityViewModel(state: EmptyState, .subscribeBy { val firstRoom = it.firstOrNull() if (firstRoom != null) { - _openRoomLiveData.postValue(Event(firstRoom.roomId)) + _openRoomLiveData.postValue(LiveEvent(firstRoom.roomId)) } } .disposeOnClear() diff --git a/app/src/main/java/im/vector/riotredesign/features/home/HomeNavigator.kt b/app/src/main/java/im/vector/riotredesign/features/home/HomeNavigator.kt index 41f61688..29cb66bc 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/HomeNavigator.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/HomeNavigator.kt @@ -23,7 +23,6 @@ class HomeNavigator { if (!addToBackstack && isRoomOpened(roomId)) { return } - currentRoomId = roomId activity?.let { val args = RoomDetailArgs(roomId, eventId) val roomDetailFragment = RoomDetailFragment.newInstance(args) @@ -31,6 +30,7 @@ class HomeNavigator { if (addToBackstack) { it.addFragmentToBackstack(roomDetailFragment, R.id.homeDetailFragmentContainer, roomId) } else { + currentRoomId = roomId clearBackStack(it.supportFragmentManager) it.replaceFragment(roomDetailFragment, R.id.homeDetailFragmentContainer) } diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt index 560e8824..d3f47e3d 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt @@ -73,7 +73,7 @@ class RoomDetailFragment : RiotFragment(), TimelineEventController.Callback { scrollOnNewMessageCallback = ScrollOnNewMessageCallback(layoutManager) recyclerView.layoutManager = layoutManager recyclerView.setHasFixedSize(true) - //timelineEventController.addModelBuildListener { it.dispatchTo(scrollOnNewMessageCallback) } + timelineEventController.addModelBuildListener { it.dispatchTo(scrollOnNewMessageCallback) } recyclerView.setController(timelineEventController) timelineEventController.callback = this } @@ -95,8 +95,15 @@ class RoomDetailFragment : RiotFragment(), TimelineEventController.Callback { private fun renderTimeline(state: RoomDetailViewState) { when (state.asyncTimelineData) { - is Success -> timelineEventController.update(state.asyncTimelineData()) + is Success -> { + val timelineData = state.asyncTimelineData() + val lockAutoScroll = timelineData?.let { + it.events == timelineEventController.currentList && it.isLoadingForward + } ?: true + scrollOnNewMessageCallback.isLocked.set(lockAutoScroll) + timelineEventController.update(timelineData) + } } } diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/ScrollOnNewMessageCallback.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/ScrollOnNewMessageCallback.kt index aec98efe..1ffbb501 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/ScrollOnNewMessageCallback.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/ScrollOnNewMessageCallback.kt @@ -6,10 +6,10 @@ import java.util.concurrent.atomic.AtomicBoolean class ScrollOnNewMessageCallback(private val layoutManager: LinearLayoutManager) : DefaultListUpdateCallback { - val hasBeenUpdated = AtomicBoolean(false) + var isLocked = AtomicBoolean(true) override fun onInserted(position: Int, count: Int) { - if (hasBeenUpdated.compareAndSet(true, false) && position == 0 && layoutManager.findFirstVisibleItemPosition() == 0) { + if (isLocked.compareAndSet(false, true) && position == 0 && layoutManager.findFirstVisibleItemPosition() == 0) { layoutManager.scrollToPosition(0) } } diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/MessageItemFactory.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/MessageItemFactory.kt index 5e5d1b1e..2122e545 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/MessageItemFactory.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/MessageItemFactory.kt @@ -4,7 +4,7 @@ import android.text.SpannableStringBuilder import android.text.util.Linkify import im.vector.matrix.android.api.permalinks.MatrixLinkify import im.vector.matrix.android.api.permalinks.MatrixPermalinkSpan -import im.vector.matrix.android.api.session.events.model.EnrichedEvent +import im.vector.matrix.android.api.session.events.model.TimelineEvent import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.room.model.MessageContent import org.threeten.bp.LocalDateTime @@ -13,8 +13,8 @@ class MessageItemFactory(private val timelineDateFormatter: TimelineDateFormatte private val messagesDisplayedWithInformation = HashSet() - fun create(event: EnrichedEvent, - nextEvent: EnrichedEvent?, + fun create(event: TimelineEvent, + nextEvent: TimelineEvent?, addDaySeparator: Boolean, date: LocalDateTime, callback: TimelineEventController.Callback? diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/TextItemFactory.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/TextItemFactory.kt index e123d7f8..1ca81862 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/TextItemFactory.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/TextItemFactory.kt @@ -1,10 +1,10 @@ package im.vector.riotredesign.features.home.room.detail.timeline -import im.vector.matrix.android.api.session.events.model.EnrichedEvent +import im.vector.matrix.android.api.session.events.model.TimelineEvent class TextItemFactory { - fun create(event: EnrichedEvent): TextItem? { + fun create(event: TimelineEvent): TextItem? { val text = "${event.root.type} events are not yet handled" return TextItem(text = text) } diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/TimelineEventController.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/TimelineEventController.kt index 8f5d70cb..0c32025c 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/TimelineEventController.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/TimelineEventController.kt @@ -2,7 +2,7 @@ package im.vector.riotredesign.features.home.room.detail.timeline import com.airbnb.epoxy.EpoxyAsyncUtil import com.airbnb.epoxy.EpoxyModel -import im.vector.matrix.android.api.session.events.model.EnrichedEvent +import im.vector.matrix.android.api.session.events.model.TimelineEvent import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.room.timeline.TimelineData import im.vector.riotredesign.core.extensions.localDateTime @@ -13,7 +13,7 @@ class TimelineEventController(private val roomId: String, private val messageItemFactory: MessageItemFactory, private val textItemFactory: TextItemFactory, private val dateFormatter: TimelineDateFormatter -) : PagedListEpoxyController( +) : PagedListEpoxyController( EpoxyAsyncUtil.getAsyncBackgroundHandler(), EpoxyAsyncUtil.getAsyncBackgroundHandler() ) { @@ -38,7 +38,7 @@ class TimelineEventController(private val roomId: String, } - override fun buildItemModels(currentPosition: Int, items: List): List> { + override fun buildItemModels(currentPosition: Int, items: List): List> { if (items.isNullOrEmpty()) { return emptyList() } diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/paging/PagedListEpoxyController.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/paging/PagedListEpoxyController.kt index a01c2904..b0bdd5a9 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/paging/PagedListEpoxyController.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/paging/PagedListEpoxyController.kt @@ -70,6 +70,9 @@ abstract class PagedListEpoxyController( modelBuildingHandler = modelBuildingHandler ) + var currentList: PagedList? = null + private set + final override fun buildModels() { addModels(modelCache.getModels()) } @@ -107,6 +110,7 @@ abstract class PagedListEpoxyController( * to [buildItemModel] with items from the previous list. */ fun submitList(newList: PagedList?) { + currentList = newList modelCache.submitList(newList) } diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/TimelineHolderTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/TimelineHolderTest.kt index 38b465d8..11555dd2 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/TimelineHolderTest.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/TimelineHolderTest.kt @@ -7,7 +7,7 @@ import im.vector.matrix.android.InstrumentedTest import im.vector.matrix.android.LiveDataTestObserver import im.vector.matrix.android.api.thread.MainThreadExecutor import im.vector.matrix.android.internal.session.room.members.RoomMemberExtractor -import im.vector.matrix.android.internal.session.room.timeline.DefaultTimelineHolder +import im.vector.matrix.android.internal.session.room.timeline.DefaultTimelineService import im.vector.matrix.android.internal.session.room.timeline.TimelineBoundaryCallback import im.vector.matrix.android.internal.session.room.timeline.TokenChunkEventPersistor import im.vector.matrix.android.internal.task.TaskExecutor @@ -44,17 +44,17 @@ internal class TimelineHolderTest : InstrumentedTest { val boundaryCallback = TimelineBoundaryCallback(roomId, taskExecutor, paginationTask, monarchy, PagingRequestHelper(MainThreadExecutor())) RoomDataHelper.fakeInitialSync(monarchy, roomId) - val timelineHolder = DefaultTimelineHolder(roomId, monarchy, taskExecutor, boundaryCallback, getContextOfEventTask, RoomMemberExtractor(monarchy, roomId)) + val timelineHolder = DefaultTimelineService(roomId, monarchy, taskExecutor, boundaryCallback, getContextOfEventTask, RoomMemberExtractor(monarchy, roomId)) val timelineObserver = LiveDataTestObserver.test(timelineHolder.timeline()) timelineObserver.awaitNextValue().assertHasValue() - var pagedList = timelineObserver.value() - pagedList.size shouldEqual 30 - (0 until pagedList.size).map { - pagedList.loadAround(it) + var timelineData = timelineObserver.value() + timelineData.events.size shouldEqual 30 + (0 until timelineData.events.size).map { + timelineData.events.loadAround(it) } timelineObserver.awaitNextValue().assertHasValue() - pagedList = timelineObserver.value() - pagedList.size shouldEqual 60 + timelineData = timelineObserver.value() + timelineData.events.size shouldEqual 60 } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt index cd4af401..d5652ef1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt @@ -15,4 +15,12 @@ interface Session : RoomService, GroupService { @MainThread fun close() + fun addListener(listener: Listener) + + fun removeListener(listener: Listener) + + // Not used at the moment + interface Listener + + } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/interceptor/EnrichedEventInterceptor.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/interceptor/EnrichedEventInterceptor.kt deleted file mode 100644 index c781f29b..00000000 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/interceptor/EnrichedEventInterceptor.kt +++ /dev/null @@ -1,12 +0,0 @@ -package im.vector.matrix.android.api.session.events.interceptor - -import im.vector.matrix.android.api.session.events.model.EnrichedEvent - -interface EnrichedEventInterceptor { - - fun canEnrich(event: EnrichedEvent): Boolean - - fun enrich(event: EnrichedEvent) - -} - diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/interceptor/TimelineEventInterceptor.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/interceptor/TimelineEventInterceptor.kt new file mode 100644 index 00000000..cf7df317 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/interceptor/TimelineEventInterceptor.kt @@ -0,0 +1,12 @@ +package im.vector.matrix.android.api.session.events.interceptor + +import im.vector.matrix.android.api.session.events.model.TimelineEvent + +interface TimelineEventInterceptor { + + fun canEnrich(event: TimelineEvent): Boolean + + fun enrich(event: TimelineEvent) + +} + diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/EnrichedEvent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/TimelineEvent.kt similarity index 95% rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/EnrichedEvent.kt rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/TimelineEvent.kt index 5cf64db9..97a5eced 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/EnrichedEvent.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/TimelineEvent.kt @@ -2,7 +2,7 @@ package im.vector.matrix.android.api.session.events.model import im.vector.matrix.android.api.session.room.model.RoomMember -data class EnrichedEvent( +data class TimelineEvent( val root: Event, val localId: String, val roomMember: RoomMember? diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineData.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineData.kt index 556c95f9..01727cd2 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineData.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineData.kt @@ -1,10 +1,10 @@ package im.vector.matrix.android.api.session.room.timeline import android.arch.paging.PagedList -import im.vector.matrix.android.api.session.events.model.EnrichedEvent +import im.vector.matrix.android.api.session.events.model.TimelineEvent data class TimelineData( - val events: PagedList, + val events: PagedList, val isLoadingForward: Boolean = false, val isLoadingBackward: Boolean = false ) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/RealmLiveEntityObserver.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/RealmLiveEntityObserver.kt index bc81a709..5f01f433 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/RealmLiveEntityObserver.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/RealmLiveEntityObserver.kt @@ -4,7 +4,6 @@ import android.arch.lifecycle.LiveData import android.arch.lifecycle.Observer import com.zhuinden.monarchy.Monarchy import io.realm.RealmObject -import io.realm.RealmResults import java.util.concurrent.atomic.AtomicBoolean internal interface LiveEntityObserver { @@ -39,11 +38,15 @@ internal abstract class RealmLiveEntityObserver(protected val m if (changeSet == null) { return } - val updateIndexes = changeSet.orderedCollectionChangeSet.changes + changeSet.orderedCollectionChangeSet.insertions + val insertionIndexes = changeSet.orderedCollectionChangeSet.insertions + val updateIndexes = changeSet.orderedCollectionChangeSet.changes val deletionIndexes = changeSet.orderedCollectionChangeSet.deletions - process(changeSet.realmResults, updateIndexes, deletionIndexes) + val inserted = changeSet.realmResults.filterIndexed { index, _ -> insertionIndexes.contains(index) } + val updated = changeSet.realmResults.filterIndexed { index, _ -> updateIndexes.contains(index) } + val deleted = changeSet.realmResults.filterIndexed { index, _ -> deletionIndexes.contains(index) } + process(inserted, updated, deleted) } - abstract fun process(results: RealmResults, updateIndexes: IntArray, deletionIndexes: IntArray) + abstract fun process(inserted: List, updated: List, deleted: List) } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt index 2d846d8b..2a723512 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt @@ -26,12 +26,13 @@ internal fun EventEntity.Companion.where(realm: Realm, query.equalTo(EventEntityFields.TYPE, type) } return when (linkFilterMode) { - LINKED_ONLY -> query.equalTo(EventEntityFields.IS_UNLINKED, false) + LINKED_ONLY -> query.equalTo(EventEntityFields.IS_UNLINKED, false) UNLINKED_ONLY -> query.equalTo(EventEntityFields.IS_UNLINKED, true) - BOTH -> query + BOTH -> query } } + internal fun RealmQuery.next(from: Int? = null, strict: Boolean = true): EventEntity? { if (from != null) { if (strict) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt index 2841f089..0c9356ed 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt @@ -31,6 +31,7 @@ internal class DefaultSession(override val sessionParams: SessionParams) : Sessi private lateinit var scope: Scope private val liveEntityUpdaters by inject>() + private val sessionListeners by inject() private val roomService by inject() private val groupService by inject() private val syncThread by inject() @@ -62,6 +63,14 @@ internal class DefaultSession(override val sessionParams: SessionParams) : Sessi isOpen = false } + override fun addListener(listener: Session.Listener) { + sessionListeners.addListener(listener) + } + + override fun removeListener(listener: Session.Listener) { + sessionListeners.removeListener(listener) + } + // ROOM SERVICE override fun getRoom(roomId: String): Room? { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionListeners.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionListeners.kt new file mode 100644 index 00000000..92c87ee4 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionListeners.kt @@ -0,0 +1,17 @@ +package im.vector.matrix.android.internal.session + +import im.vector.matrix.android.api.session.Session + +internal class SessionListeners { + + private val listeners = ArrayList() + + fun addListener(listener: Session.Listener) { + listeners.add(listener) + } + + fun removeListener(listener: Session.Listener) { + listeners.remove(listener) + } + +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt index 41644ad8..d36cbcbf 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt @@ -6,7 +6,6 @@ import im.vector.matrix.android.api.auth.data.SessionParams import im.vector.matrix.android.api.session.group.GroupService import im.vector.matrix.android.api.session.room.RoomService import im.vector.matrix.android.internal.database.LiveEntityObserver -import im.vector.matrix.android.internal.session.room.prune.EventsPruner import im.vector.matrix.android.internal.session.group.DefaultGroupService import im.vector.matrix.android.internal.session.group.GroupSummaryUpdater import im.vector.matrix.android.internal.session.room.DefaultRoomService @@ -14,6 +13,7 @@ import im.vector.matrix.android.internal.session.room.RoomAvatarResolver import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater import im.vector.matrix.android.internal.session.room.members.RoomDisplayNameResolver import im.vector.matrix.android.internal.session.room.members.RoomMemberDisplayNameResolver +import im.vector.matrix.android.internal.session.room.prune.EventsPruner import im.vector.matrix.android.internal.util.md5 import io.realm.RealmConfiguration import org.koin.dsl.module.module @@ -75,7 +75,10 @@ internal class SessionModule(private val sessionParams: SessionParams) { } scope(DefaultSession.SCOPE) { + SessionListeners() + } + scope(DefaultSession.SCOPE) { val roomSummaryUpdater = RoomSummaryUpdater(get(), get(), get(), get(), sessionParams.credentials) val groupSummaryUpdater = GroupSummaryUpdater(get()) val eventsPruner = EventsPruner(get()) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupDataWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupDataWorker.kt index fc5c0d1e..861e9640 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupDataWorker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupDataWorker.kt @@ -15,9 +15,7 @@ internal class GetGroupDataWorker(context: Context, @JsonClass(generateAdapter = true) internal data class Params( - val groupIds: List, - val updateIndexes: List, - val deletionIndexes: List + val groupIds: List ) private val getGroupDataTask by inject() @@ -26,8 +24,7 @@ internal class GetGroupDataWorker(context: Context, val params = WorkerParamsFactory.fromData(inputData) ?: return Result.failure() - val results = params.updateIndexes.map { index -> - val groupId = params.groupIds[index] + val results = params.groupIds.map { groupId -> fetchGroupData(groupId) } val isSuccessful = results.none { it.isFailure() } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupSummaryUpdater.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupSummaryUpdater.kt index be70a160..7d5d4f3d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupSummaryUpdater.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupSummaryUpdater.kt @@ -1,12 +1,15 @@ package im.vector.matrix.android.internal.session.group -import androidx.work.* +import androidx.work.Constraints +import androidx.work.ExistingWorkPolicy +import androidx.work.NetworkType +import androidx.work.OneTimeWorkRequestBuilder +import androidx.work.WorkManager import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.internal.database.RealmLiveEntityObserver import im.vector.matrix.android.internal.database.model.GroupEntity import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.util.WorkerParamsFactory -import io.realm.RealmResults private const val GET_GROUP_DATA_WORKER = "GET_GROUP_DATA_WORKER" @@ -19,9 +22,9 @@ internal class GroupSummaryUpdater(monarchy: Monarchy .setRequiredNetworkType(NetworkType.CONNECTED) .build() - override fun process(results: RealmResults, updateIndexes: IntArray, deletionIndexes: IntArray) { - val groupIds = results.map { it.groupId } - val getGroupDataWorkerParams = GetGroupDataWorker.Params(groupIds, updateIndexes.toList(), deletionIndexes.toList()) + override fun process(inserted: List, updated: List, deleted: List) { + val newGroupIds = inserted.map { it.groupId } + val getGroupDataWorkerParams = GetGroupDataWorker.Params(newGroupIds) val workData = WorkerParamsFactory.toData(getGroupDataWorkerParams) val sendWork = OneTimeWorkRequestBuilder() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoom.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoom.kt index 4cf645a5..2359c301 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoom.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoom.kt @@ -2,24 +2,23 @@ package im.vector.matrix.android.internal.session.room import android.arch.lifecycle.LiveData import android.arch.lifecycle.Transformations -import android.arch.paging.PagedList import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.MatrixCallback -import im.vector.matrix.android.api.session.events.model.EnrichedEvent import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.room.Room import im.vector.matrix.android.api.session.room.SendService -import im.vector.matrix.android.api.session.room.timeline.TimelineService import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.android.api.session.room.model.MyMembership import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.timeline.TimelineData +import im.vector.matrix.android.api.session.room.timeline.TimelineService import im.vector.matrix.android.api.util.Cancelable import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.model.RoomSummaryEntity import im.vector.matrix.android.internal.database.model.RoomSummaryEntityFields import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.di.MatrixKoinComponent +import im.vector.matrix.android.internal.session.SessionListeners import im.vector.matrix.android.internal.session.room.members.LoadRoomMembersTask import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith @@ -62,4 +61,5 @@ internal data class DefaultRoom( return sendService.sendTextMessage(text, callback) } + } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt index 37fe93c1..c0e9863c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt @@ -17,7 +17,6 @@ import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.session.room.members.RoomDisplayNameResolver import im.vector.matrix.android.internal.session.room.members.RoomMembers import io.realm.Realm -import io.realm.RealmResults import io.realm.kotlin.createObject internal class RoomSummaryUpdater(monarchy: Monarchy, @@ -29,13 +28,10 @@ internal class RoomSummaryUpdater(monarchy: Monarchy, override val query = Monarchy.Query { RoomEntity.where(it) } - override fun process(results: RealmResults, updateIndexes: IntArray, deletionIndexes: IntArray) { - val rooms = results.map { it.asDomain() } + override fun process(inserted: List, updated: List, deleted: List) { + val rooms = (inserted + updated).map { it.asDomain() } monarchy.writeAsync { realm -> - updateIndexes.forEach { index -> - val data = rooms[index] - updateRoom(realm, data) - } + rooms.forEach { updateRoom(realm, it) } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/EventsPruner.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/EventsPruner.kt index a80b7ccc..ea5d8d64 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/EventsPruner.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/EventsPruner.kt @@ -10,7 +10,6 @@ import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.util.WorkerParamsFactory -import io.realm.RealmResults private const val PRUNE_EVENT_WORKER = "PRUNE_EVENT_WORKER" @@ -19,9 +18,9 @@ internal class EventsPruner(monarchy: Monarchy) : override val query = Monarchy.Query { EventEntity.where(it, type = EventType.REDACTION) } - override fun process(results: RealmResults, updateIndexes: IntArray, deletionIndexes: IntArray) { - val redactionEvents = results.map { it.asDomain() } - val pruneEventWorkerParams = PruneEventWorker.Params(redactionEvents, updateIndexes.toList(), deletionIndexes.toList()) + override fun process(inserted: List, updated: List, deleted: List) { + val redactionEvents = inserted.map { it.asDomain() } + val pruneEventWorkerParams = PruneEventWorker.Params(redactionEvents) val workData = WorkerParamsFactory.toData(pruneEventWorkerParams) val sendWork = OneTimeWorkRequestBuilder() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/PruneEventWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/PruneEventWorker.kt index 6562fd14..90e5cdc4 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/PruneEventWorker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/PruneEventWorker.kt @@ -13,6 +13,7 @@ import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.di.MatrixKoinComponent import im.vector.matrix.android.internal.util.WorkerParamsFactory import im.vector.matrix.android.internal.util.tryTransactionAsync +import im.vector.matrix.android.internal.util.tryTransactionSync import io.realm.Realm import org.koin.standalone.inject @@ -22,9 +23,7 @@ internal class PruneEventWorker(context: Context, @JsonClass(generateAdapter = true) internal data class Params( - val redactionEvents: List, - val updateIndexes: List, - val deletionIndexes: List + val redactionEvents: List ) private val monarchy by inject() @@ -33,10 +32,9 @@ internal class PruneEventWorker(context: Context, val params = WorkerParamsFactory.fromData(inputData) ?: return Result.failure() - val result = monarchy.tryTransactionAsync { realm -> - params.updateIndexes.forEach { index -> - val data = params.redactionEvents[index] - pruneEvent(realm, data) + val result = monarchy.tryTransactionSync { realm -> + params.redactionEvents.forEach { event -> + pruneEvent(realm, event) } } return result.fold({ Result.retry() }, { Result.success() }) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineService.kt index 6f5a80ad..2a06dbb4 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineService.kt @@ -4,8 +4,8 @@ import android.arch.lifecycle.LiveData import android.arch.paging.LivePagedListBuilder import android.arch.paging.PagedList import com.zhuinden.monarchy.Monarchy -import im.vector.matrix.android.api.session.events.interceptor.EnrichedEventInterceptor -import im.vector.matrix.android.api.session.events.model.EnrichedEvent +import im.vector.matrix.android.api.session.events.interceptor.TimelineEventInterceptor +import im.vector.matrix.android.api.session.events.model.TimelineEvent import im.vector.matrix.android.api.session.room.timeline.TimelineData import im.vector.matrix.android.api.session.room.timeline.TimelineService import im.vector.matrix.android.internal.database.mapper.asDomain @@ -34,35 +34,21 @@ internal class DefaultTimelineService(private val roomId: String, private val roomMemberExtractor: RoomMemberExtractor ) : TimelineService { - private val eventInterceptors = ArrayList() + private val eventInterceptors = ArrayList() override fun timeline(eventId: String?): LiveData { clearUnlinkedEvents() - var initialLoadKey = 0 - if (eventId != null) { - val indexOfEvent = indexOfEvent(eventId) - if (indexOfEvent == EVENT_NOT_FOUND_INDEX) { - val params = GetContextOfEventTask.Params(roomId, eventId) - contextOfEventTask.configureWith(params).executeBy(taskExecutor) - } else { - initialLoadKey = indexOfEvent - } - } + val initialLoadKey = getInitialLoadKey(eventId) val realmDataSourceFactory = monarchy.createDataSourceFactory { buildDataSourceFactoryQuery(it, eventId) } val domainSourceFactory = realmDataSourceFactory .map { eventEntity -> val roomMember = roomMemberExtractor.extractFrom(eventEntity) - EnrichedEvent(eventEntity.asDomain(), eventEntity.localId, roomMember) + TimelineEvent(eventEntity.asDomain(), eventEntity.localId, roomMember) } - val pagedListConfig = PagedList.Config.Builder() - .setEnablePlaceholders(false) - .setPageSize(PAGE_SIZE) - .setInitialLoadSizeHint(2 * PAGE_SIZE) - .setPrefetchDistance(PREFETCH_DISTANCE) - .build() + val pagedListConfig = buildPagedListConfig() val livePagedListBuilder = LivePagedListBuilder(domainSourceFactory, pagedListConfig) .setBoundaryCallback(boundaryCallback) @@ -77,6 +63,35 @@ internal class DefaultTimelineService(private val roomId: String, } } + // PRIVATE FUNCTIONS *************************************************************************** + + private fun getInitialLoadKey(eventId: String?): Int { + var initialLoadKey = 0 + if (eventId != null) { + val indexOfEvent = indexOfEvent(eventId) + if (indexOfEvent == EVENT_NOT_FOUND_INDEX) { + fetchEvent(eventId) + } else { + initialLoadKey = indexOfEvent + } + } + return initialLoadKey + } + + + private fun fetchEvent(eventId: String) { + val params = GetContextOfEventTask.Params(roomId, eventId) + contextOfEventTask.configureWith(params).executeBy(taskExecutor) + } + + private fun buildPagedListConfig(): PagedList.Config { + return PagedList.Config.Builder() + .setEnablePlaceholders(false) + .setPageSize(PAGE_SIZE) + .setInitialLoadSizeHint(2 * PAGE_SIZE) + .setPrefetchDistance(PREFETCH_DISTANCE) + .build() + } private fun clearUnlinkedEvents() { monarchy.tryTransactionAsync { realm -> diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineBoundaryCallback.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineBoundaryCallback.kt index 2ce550f3..88282e85 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineBoundaryCallback.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineBoundaryCallback.kt @@ -4,7 +4,7 @@ import android.arch.lifecycle.LiveData import android.arch.paging.PagedList import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.MatrixCallback -import im.vector.matrix.android.api.session.events.model.EnrichedEvent +import im.vector.matrix.android.api.session.events.model.TimelineEvent import im.vector.matrix.android.internal.database.model.ChunkEntity import im.vector.matrix.android.internal.database.query.findIncludingEvent import im.vector.matrix.android.internal.task.TaskExecutor @@ -16,7 +16,7 @@ internal class TimelineBoundaryCallback(private val roomId: String, private val paginationTask: PaginationTask, private val monarchy: Monarchy, private val helper: PagingRequestHelper -) : PagedList.BoundaryCallback() { +) : PagedList.BoundaryCallback() { var limit = 30 @@ -41,7 +41,7 @@ internal class TimelineBoundaryCallback(private val roomId: String, // actually, it's not possible } - override fun onItemAtEndLoaded(itemAtEnd: EnrichedEvent) { + override fun onItemAtEndLoaded(itemAtEnd: TimelineEvent) { val token = itemAtEnd.root.eventId?.let { getToken(it, PaginationDirection.BACKWARDS) } ?: return @@ -50,7 +50,7 @@ internal class TimelineBoundaryCallback(private val roomId: String, } } - override fun onItemAtFrontLoaded(itemAtFront: EnrichedEvent) { + override fun onItemAtFrontLoaded(itemAtFront: TimelineEvent) { val token = itemAtFront.root.eventId?.let { getToken(it, PaginationDirection.FORWARDS) } ?: return