Timeline: fix some more issues
This commit is contained in:
parent
4a80df082c
commit
0ea878af8a
|
@ -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) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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).
|
||||||
|
|
|
@ -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).
|
||||||
|
|
|
@ -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 }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -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 {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue