Timeline: fix some more issues

This commit is contained in:
ganfra 2019-09-26 11:55:16 +02:00
parent 4a80df082c
commit 0ea878af8a
13 changed files with 93 additions and 73 deletions

View File

@ -198,9 +198,9 @@ internal class DefaultTimeline(
.also { it.addChangeListener(relationsListener) } .also { it.addChangeListener(relationsListener) }
if (settings.buildReadReceipts) { if (settings.buildReadReceipts) {
hiddenReadReceipts.start(realm, filteredEvents, this) hiddenReadReceipts.start(realm, filteredEvents, nonFilteredEvents, this)
} }
hiddenReadMarker.start(realm, filteredEvents, this) hiddenReadMarker.start(realm, filteredEvents, nonFilteredEvents, this)
isReady.set(true) isReady.set(true)
} }
} }
@ -490,9 +490,9 @@ internal class DefaultTimeline(
return return
} }
val params = PaginationTask.Params(roomId = roomId, val params = PaginationTask.Params(roomId = roomId,
from = token, from = token,
direction = direction.toPaginationDirection(), direction = direction.toPaginationDirection(),
limit = limit) limit = limit)
Timber.v("Should fetch $limit items $direction") Timber.v("Should fetch $limit items $direction")
cancelableBag += paginationTask cancelableBag += paginationTask
@ -563,7 +563,7 @@ internal class DefaultTimeline(
val timelineEvent = buildTimelineEvent(eventEntity) val timelineEvent = buildTimelineEvent(eventEntity)
if (timelineEvent.isEncrypted() if (timelineEvent.isEncrypted()
&& timelineEvent.root.mxDecryptionResult == null) { && timelineEvent.root.mxDecryptionResult == null) {
timelineEvent.root.eventId?.let { eventDecryptor.requestDecryption(it) } timelineEvent.root.eventId?.let { eventDecryptor.requestDecryption(it) }
} }

View File

@ -46,7 +46,8 @@ internal class TimelineHiddenReadMarker constructor(private val roomId: String,
private var previousDisplayedEventId: String? = null private var previousDisplayedEventId: String? = null
private var hiddenReadMarker: RealmResults<ReadMarkerEntity>? = null private var hiddenReadMarker: RealmResults<ReadMarkerEntity>? = null
private lateinit var liveEvents: RealmResults<TimelineEventEntity> private lateinit var filteredEvents: RealmResults<TimelineEventEntity>
private lateinit var nonFilteredEvents: RealmResults<TimelineEventEntity>
private lateinit var delegate: Delegate private lateinit var delegate: Delegate
private val readMarkerListener = OrderedRealmCollectionChangeListener<RealmResults<ReadMarkerEntity>> { readMarkers, changeSet -> private val readMarkerListener = OrderedRealmCollectionChangeListener<RealmResults<ReadMarkerEntity>> { readMarkers, changeSet ->
@ -62,12 +63,13 @@ internal class TimelineHiddenReadMarker constructor(private val roomId: String,
} }
val readMarker = readMarkers.firstOrNull() ?: return@OrderedRealmCollectionChangeListener val readMarker = readMarkers.firstOrNull() ?: return@OrderedRealmCollectionChangeListener
val hiddenEvent = readMarker.timelineEvent?.firstOrNull() val hiddenEvent = readMarker.timelineEvent?.firstOrNull()
?: return@OrderedRealmCollectionChangeListener ?: return@OrderedRealmCollectionChangeListener
val isLoaded = nonFilteredEvents.where().equalTo(TimelineEventEntityFields.EVENT_ID, hiddenEvent.eventId).findFirst() != null
val displayIndex = hiddenEvent.root?.displayIndex val displayIndex = hiddenEvent.root?.displayIndex
if (displayIndex != null) { if (isLoaded && displayIndex != null) {
// Then we are looking for the first displayable event after the hidden one // Then we are looking for the first displayable event after the hidden one
val firstDisplayedEvent = liveEvents.where() val firstDisplayedEvent = filteredEvents.where()
.lessThanOrEqualTo(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, displayIndex) .lessThanOrEqualTo(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, displayIndex)
.findFirst() .findFirst()
@ -86,8 +88,12 @@ internal class TimelineHiddenReadMarker constructor(private val roomId: String,
/** /**
* Start the realm query subscription. Has to be called on an HandlerThread * Start the realm query subscription. Has to be called on an HandlerThread
*/ */
fun start(realm: Realm, liveEvents: RealmResults<TimelineEventEntity>, delegate: Delegate) { fun start(realm: Realm,
this.liveEvents = liveEvents filteredEvents: RealmResults<TimelineEventEntity>,
nonFilteredEvents: RealmResults<TimelineEventEntity>,
delegate: Delegate) {
this.filteredEvents = filteredEvents
this.nonFilteredEvents = nonFilteredEvents
this.delegate = delegate this.delegate = delegate
// We are looking for read receipts set on hidden events. // We are looking for read receipts set on hidden events.
// We only accept those with a timelineEvent (so coming from pagination/sync). // We only accept those with a timelineEvent (so coming from pagination/sync).

View File

@ -49,7 +49,8 @@ internal class TimelineHiddenReadReceipts constructor(private val readReceiptsSu
private val correctedReadReceiptsByEvent = HashMap<String, MutableList<ReadReceipt>>() private val correctedReadReceiptsByEvent = HashMap<String, MutableList<ReadReceipt>>()
private lateinit var hiddenReadReceipts: RealmResults<ReadReceiptsSummaryEntity> private lateinit var hiddenReadReceipts: RealmResults<ReadReceiptsSummaryEntity>
private lateinit var liveEvents: RealmResults<TimelineEventEntity> private lateinit var nonFilteredEvents: RealmResults<TimelineEventEntity>
private lateinit var filteredEvents: RealmResults<TimelineEventEntity>
private lateinit var delegate: Delegate private lateinit var delegate: Delegate
private val hiddenReadReceiptsListener = OrderedRealmCollectionChangeListener<RealmResults<ReadReceiptsSummaryEntity>> { collection, changeSet -> private val hiddenReadReceiptsListener = OrderedRealmCollectionChangeListener<RealmResults<ReadReceiptsSummaryEntity>> { collection, changeSet ->
@ -60,7 +61,7 @@ internal class TimelineHiddenReadReceipts constructor(private val readReceiptsSu
// Deletion here means we don't have any readReceipts for the given hidden events // Deletion here means we don't have any readReceipts for the given hidden events
changeSet.deletions.forEach { changeSet.deletions.forEach {
val eventId = correctedReadReceiptsEventByIndex.get(it, "") val eventId = correctedReadReceiptsEventByIndex.get(it, "")
val timelineEvent = liveEvents.where() val timelineEvent = filteredEvents.where()
.equalTo(TimelineEventEntityFields.EVENT_ID, eventId) .equalTo(TimelineEventEntityFields.EVENT_ID, eventId)
.findFirst() .findFirst()
@ -70,12 +71,14 @@ internal class TimelineHiddenReadReceipts constructor(private val readReceiptsSu
} }
correctedReadReceiptsEventByIndex.clear() correctedReadReceiptsEventByIndex.clear()
correctedReadReceiptsByEvent.clear() correctedReadReceiptsByEvent.clear()
hiddenReadReceipts.forEachIndexed { index, summary -> for (index in 0 until hiddenReadReceipts.size) {
val timelineEvent = summary?.timelineEvent?.firstOrNull() val summary = hiddenReadReceipts[index] ?: continue
val displayIndex = timelineEvent?.root?.displayIndex val timelineEvent = summary.timelineEvent?.firstOrNull() ?: continue
if (displayIndex != null) { val isLoaded = nonFilteredEvents.where().equalTo(TimelineEventEntityFields.EVENT_ID, timelineEvent.eventId).findFirst() != null
val displayIndex = timelineEvent.root?.displayIndex
if (isLoaded && displayIndex != null) {
// Then we are looking for the first displayable event after the hidden one // Then we are looking for the first displayable event after the hidden one
val firstDisplayedEvent = liveEvents.where() val firstDisplayedEvent = filteredEvents.where()
.lessThanOrEqualTo(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, displayIndex) .lessThanOrEqualTo(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, displayIndex)
.findFirst() .findFirst()
@ -106,8 +109,12 @@ internal class TimelineHiddenReadReceipts constructor(private val readReceiptsSu
/** /**
* Start the realm query subscription. Has to be called on an HandlerThread * Start the realm query subscription. Has to be called on an HandlerThread
*/ */
fun start(realm: Realm, liveEvents: RealmResults<TimelineEventEntity>, delegate: Delegate) { fun start(realm: Realm,
this.liveEvents = liveEvents filteredEvents: RealmResults<TimelineEventEntity>,
nonFilteredEvents: RealmResults<TimelineEventEntity>,
delegate: Delegate) {
this.filteredEvents = filteredEvents
this.nonFilteredEvents = nonFilteredEvents
this.delegate = delegate this.delegate = delegate
// We are looking for read receipts set on hidden events. // We are looking for read receipts set on hidden events.
// We only accept those with a timelineEvent (so coming from pagination/sync). // We only accept those with a timelineEvent (so coming from pagination/sync).

View File

@ -40,14 +40,18 @@ internal class RoomFullyReadHandler @Inject constructor() {
readMarkerId = content.eventId readMarkerId = content.eventId
} }
// Remove the old markers if any // Remove the old markers if any
val oldReadMarkerEvents = TimelineEventEntity.where(realm, roomId = roomId, linkFilterMode = EventEntity.LinkFilterMode.BOTH).isNotNull(TimelineEventEntityFields.READ_MARKER.`$`).findAll() val oldReadMarkerEvents = TimelineEventEntity
.where(realm, roomId = roomId, linkFilterMode = EventEntity.LinkFilterMode.BOTH)
.isNotNull(TimelineEventEntityFields.READ_MARKER.`$`)
.findAll()
oldReadMarkerEvents.forEach { it.readMarker = null } oldReadMarkerEvents.forEach { it.readMarker = null }
val readMarkerEntity = ReadMarkerEntity.getOrCreate(realm, roomId).apply { val readMarkerEntity = ReadMarkerEntity.getOrCreate(realm, roomId).apply {
this.eventId = content.eventId this.eventId = content.eventId
} }
// Attach to timelineEvent if known // Attach to timelineEvent if known
val timelineEventEntity = TimelineEventEntity.where(realm, roomId = roomId, eventId = content.eventId).findFirst() val timelineEventEntities = TimelineEventEntity.where(realm, roomId = roomId, eventId = content.eventId).findAll()
timelineEventEntity?.readMarker = readMarkerEntity timelineEventEntities.forEach { it.readMarker = readMarkerEntity }
} }
} }

View File

@ -80,7 +80,9 @@ class ReadMarkerHelper @Inject constructor() {
val newJumpToReadMarkerVisible = if (readMarkerId == null) { val newJumpToReadMarkerVisible = if (readMarkerId == null) {
false false
} else { } else {
val positionOfReadMarker = timelineEventController.searchPositionOfEvent(readMarkerId) val correctedReadMarkerId = nonNullState.timeline?.getFirstDisplayableEventId(readMarkerId)
?: readMarkerId
val positionOfReadMarker = timelineEventController.searchPositionOfEvent(correctedReadMarkerId)
if (positionOfReadMarker == null) { if (positionOfReadMarker == null) {
nonNullState.timeline?.isLive == true && lastVisibleItem > 0 nonNullState.timeline?.isLive == true && lastVisibleItem > 0
} else { } else {

View File

@ -250,6 +250,7 @@ class RoomDetailFragment :
if (scrollPosition == null) { if (scrollPosition == null) {
scrollOnHighlightedEventCallback.scheduleScrollTo(it) scrollOnHighlightedEventCallback.scheduleScrollTo(it)
} else { } else {
recyclerView.stopScroll()
layoutManager.scrollToPosition(scrollPosition) layoutManager.scrollToPosition(scrollPosition)
} }
} }
@ -445,7 +446,7 @@ class RoomDetailFragment :
layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, true) layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, true)
val stateRestorer = LayoutManagerStateRestorer(layoutManager).register() val stateRestorer = LayoutManagerStateRestorer(layoutManager).register()
scrollOnNewMessageCallback = ScrollOnNewMessageCallback(layoutManager, timelineEventController) scrollOnNewMessageCallback = ScrollOnNewMessageCallback(layoutManager, timelineEventController)
scrollOnHighlightedEventCallback = ScrollOnHighlightedEventCallback(layoutManager, timelineEventController) scrollOnHighlightedEventCallback = ScrollOnHighlightedEventCallback(recyclerView, layoutManager, timelineEventController)
recyclerView.layoutManager = layoutManager recyclerView.layoutManager = layoutManager
recyclerView.itemAnimator = null recyclerView.itemAnimator = null
recyclerView.setHasFixedSize(true) recyclerView.setHasFixedSize(true)
@ -958,7 +959,7 @@ class RoomDetailFragment :
val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition() val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition()
val firstVisibleItem = timelineEventController.adapter.getModelAtPosition(firstVisibleItemPosition) val firstVisibleItem = timelineEventController.adapter.getModelAtPosition(firstVisibleItemPosition)
val nextReadMarkerId = when (firstVisibleItem) { val nextReadMarkerId = when (firstVisibleItem) {
is BaseEventItem -> firstVisibleItem.getEventId() is BaseEventItem -> firstVisibleItem.getEventIds().firstOrNull()
else -> null else -> null
} }
if (nextReadMarkerId != null) { if (nextReadMarkerId != null) {

View File

@ -17,9 +17,11 @@
package im.vector.riotx.features.home.room.detail package im.vector.riotx.features.home.room.detail
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import im.vector.matrix.android.api.session.room.timeline.Timeline import im.vector.matrix.android.api.session.room.timeline.Timeline
import im.vector.riotx.core.platform.DefaultListUpdateCallback import im.vector.riotx.core.platform.DefaultListUpdateCallback
import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController
import kotlinx.android.synthetic.main.fragment_room_detail.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -29,7 +31,8 @@ import java.util.concurrent.atomic.AtomicReference
/** /**
* This handles scrolling to an event which wasn't yet loaded when scheduled. * This handles scrolling to an event which wasn't yet loaded when scheduled.
*/ */
class ScrollOnHighlightedEventCallback(private val layoutManager: LinearLayoutManager, class ScrollOnHighlightedEventCallback(private val recyclerView: RecyclerView,
private val layoutManager: LinearLayoutManager,
private val timelineEventController: TimelineEventController) : DefaultListUpdateCallback { private val timelineEventController: TimelineEventController) : DefaultListUpdateCallback {
private val scheduledEventId = AtomicReference<String?>() private val scheduledEventId = AtomicReference<String?>()
@ -56,6 +59,7 @@ class ScrollOnHighlightedEventCallback(private val layoutManager: LinearLayoutMa
// Do not scroll it item is already visible // Do not scroll it item is already visible
if (positionToScroll !in firstVisibleItem..lastVisibleItem) { if (positionToScroll !in firstVisibleItem..lastVisibleItem) {
Timber.v("Scroll to $positionToScroll") Timber.v("Scroll to $positionToScroll")
recyclerView.stopScroll()
layoutManager.scrollToPosition(positionToScroll) layoutManager.scrollToPosition(positionToScroll)
} }
scheduledEventId.set(null) scheduledEventId.set(null)

View File

@ -50,7 +50,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
private val mergedHeaderItemFactory: MergedHeaderItemFactory, private val mergedHeaderItemFactory: MergedHeaderItemFactory,
@TimelineEventControllerHandler @TimelineEventControllerHandler
private val backgroundHandler: Handler private val backgroundHandler: Handler
) : EpoxyController(backgroundHandler, backgroundHandler), Timeline.Listener { ) : EpoxyController(backgroundHandler, backgroundHandler), Timeline.Listener, EpoxyController.Interceptor {
interface Callback : BaseCallback, ReactionPillCallback, AvatarCallback, UrlClickCallback, ReadReceiptsCallback { interface Callback : BaseCallback, ReactionPillCallback, AvatarCallback, UrlClickCallback, ReadReceiptsCallback {
fun onLoadMore(direction: Timeline.Direction) fun onLoadMore(direction: Timeline.Direction)
@ -91,6 +91,8 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
} }
private var showingForwardLoader = false private var showingForwardLoader = false
// Map eventId to adapter position
private val adapterPositionMapping = HashMap<String, Int>()
private val modelCache = arrayListOf<CacheItemData?>() private val modelCache = arrayListOf<CacheItemData?>()
private var currentSnapshot: List<TimelineEvent> = emptyList() private var currentSnapshot: List<TimelineEvent> = emptyList()
private var inSubmitList: Boolean = false private var inSubmitList: Boolean = false
@ -98,6 +100,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
var callback: Callback? = null var callback: Callback? = null
private val listUpdateCallback = object : ListUpdateCallback { private val listUpdateCallback = object : ListUpdateCallback {
override fun onChanged(position: Int, count: Int, payload: Any?) { override fun onChanged(position: Int, count: Int, payload: Any?) {
@ -141,9 +144,22 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
} }
init { init {
addInterceptor(this)
requestModelBuild() requestModelBuild()
} }
// Update position when we are building new items
override fun intercept(models: MutableList<EpoxyModel<*>>) {
adapterPositionMapping.clear()
models.forEachIndexed { index, epoxyModel ->
if (epoxyModel is BaseEventItem) {
epoxyModel.getEventIds().forEach {
adapterPositionMapping[it] = index
}
}
}
}
fun update(viewState: RoomDetailViewState, readMarkerVisible: Boolean) { fun update(viewState: RoomDetailViewState, readMarkerVisible: Boolean) {
if (timeline != viewState.timeline) { if (timeline != viewState.timeline) {
timeline = viewState.timeline timeline = viewState.timeline
@ -161,7 +177,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
synchronized(modelCache) { synchronized(modelCache) {
for (i in 0 until modelCache.size) { for (i in 0 until modelCache.size) {
if (modelCache[i]?.eventId == viewState.highlightedEventId if (modelCache[i]?.eventId == viewState.highlightedEventId
|| modelCache[i]?.eventId == eventIdToHighlight) { || modelCache[i]?.eventId == eventIdToHighlight) {
modelCache[i] = null modelCache[i] = null
} }
} }
@ -186,6 +202,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
timelineMediaSizeProvider.recyclerView = recyclerView timelineMediaSizeProvider.recyclerView = recyclerView
} }
override fun buildModels() { override fun buildModels() {
val timestamp = System.currentTimeMillis() val timestamp = System.currentTimeMillis()
showingForwardLoader = LoadingItem_() showingForwardLoader = LoadingItem_()
@ -232,8 +249,8 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
// Should be build if not cached or if cached but contains mergedHeader or formattedDay // Should be build if not cached or if cached but contains mergedHeader or formattedDay
// We then are sure we always have items up to date. // We then are sure we always have items up to date.
if (modelCache[position] == null if (modelCache[position] == null
|| modelCache[position]?.mergedHeaderModel != null || modelCache[position]?.mergedHeaderModel != null
|| modelCache[position]?.formattedDayModel != null) { || modelCache[position]?.formattedDayModel != null) {
modelCache[position] = buildItemModels(position, currentSnapshot) modelCache[position] = buildItemModels(position, currentSnapshot)
} }
} }
@ -269,13 +286,13 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
it.setOnVisibilityStateChanged(TimelineEventVisibilityStateChangedListener(callback, event)) it.setOnVisibilityStateChanged(TimelineEventVisibilityStateChangedListener(callback, event))
} }
val mergedHeaderModel = mergedHeaderItemFactory.create(event, val mergedHeaderModel = mergedHeaderItemFactory.create(event,
nextEvent = nextEvent, nextEvent = nextEvent,
items = items, items = items,
addDaySeparator = addDaySeparator, addDaySeparator = addDaySeparator,
readMarkerVisible = readMarkerVisible, readMarkerVisible = readMarkerVisible,
currentPosition = currentPosition, currentPosition = currentPosition,
eventIdToHighlight = eventIdToHighlight, eventIdToHighlight = eventIdToHighlight,
callback = callback callback = callback
) { ) {
requestModelBuild() requestModelBuild()
} }
@ -314,30 +331,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
} }
fun searchPositionOfEvent(eventId: String?): Int? = synchronized(modelCache) { fun searchPositionOfEvent(eventId: String?): Int? = synchronized(modelCache) {
// Search in the cache return adapterPositionMapping[eventId]
if (eventId == null) {
return null
}
var realPosition = 0
if (showingForwardLoader) {
realPosition++
}
for (i in 0 until modelCache.size) {
val itemCache = modelCache[i] ?: continue
if (itemCache.eventId == eventId) {
return realPosition
}
if (itemCache.eventModel != null && !mergedHeaderItemFactory.isCollapsed(itemCache.localId)) {
realPosition++
}
if (itemCache.mergedHeaderModel != null) {
realPosition++
}
if (itemCache.formattedDayModel != null) {
realPosition++
}
}
return null
} }
fun isLoadingForward() = showingForwardLoader fun isLoadingForward() = showingForwardLoader

View File

@ -29,7 +29,6 @@ import androidx.core.view.children
import androidx.core.view.isGone import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.VisibilityState
import im.vector.matrix.android.api.session.room.send.SendState import im.vector.matrix.android.api.session.room.send.SendState
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.resources.ColorProvider import im.vector.riotx.core.resources.ColorProvider
@ -158,8 +157,8 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : BaseEventItem<H>() {
return true return true
} }
override fun getEventId(): String? { override fun getEventIds(): List<String> {
return attributes.informationData.eventId return listOf(attributes.informationData.eventId)
} }
protected open fun renderSendState(root: View, textView: TextView?, failureIndicator: ImageView? = null) { protected open fun renderSendState(root: View, textView: TextView?, failureIndicator: ImageView? = null) {

View File

@ -44,12 +44,15 @@ abstract class BaseEventItem<H : BaseEventItem.BaseHolder> : VectorEpoxyModel<H>
override fun bind(holder: H) { override fun bind(holder: H) {
super.bind(holder) super.bind(holder)
holder
holder.leftGuideline.setGuidelineBegin(leftGuideline) holder.leftGuideline.setGuidelineBegin(leftGuideline)
holder.checkableBackground.isChecked = highlighted holder.checkableBackground.isChecked = highlighted
} }
abstract fun getEventId(): String? /**
* Returns the eventIds associated with the EventItem.
* Will generally get only one, but it handles the merging items.
*/
abstract fun getEventIds(): List<String>
abstract class BaseHolder(@IdRes val stubId: Int) : VectorEpoxyHolder() { abstract class BaseHolder(@IdRes val stubId: Int) : VectorEpoxyHolder() {
val leftGuideline by bind<Guideline>(R.id.messageStartGuideline) val leftGuideline by bind<Guideline>(R.id.messageStartGuideline)

View File

@ -55,7 +55,7 @@ abstract class DefaultItem : BaseEventItem<DefaultItem.Holder>() {
holder.readReceiptsView.render(informationData.readReceipts, avatarRenderer, _readReceiptsClickListener) holder.readReceiptsView.render(informationData.readReceipts, avatarRenderer, _readReceiptsClickListener)
} }
override fun getEventId(): String? { override fun getEventIds(): List<String> {
return informationData.eventId return informationData.eventId
} }

View File

@ -90,8 +90,8 @@ abstract class MergedHeaderItem : BaseEventItem<MergedHeaderItem.Holder>() {
} }
override fun getEventId(): String? { override fun getEventIds(): List<String> {
return attributes.mergeData.firstOrNull()?.eventId return attributes.mergeData.map { it.eventId }
} }
data class Data( data class Data(

View File

@ -70,8 +70,8 @@ abstract class NoticeItem : BaseEventItem<NoticeItem.Holder>() {
} }
override fun getEventId(): String? { override fun getEventIds(): List<String> {
return attributes.informationData.eventId return listOf(attributes.informationData.eventId)
} }
override fun getViewType() = STUB_ID override fun getViewType() = STUB_ID