mirror of
https://github.com/element-hq/element-android
synced 2024-11-28 05:31:21 +03:00
Read marker: don't show unread on events we own
This commit is contained in:
parent
5e07e96bdb
commit
ab489df83d
2 changed files with 41 additions and 24 deletions
|
@ -25,6 +25,7 @@ import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.airbnb.epoxy.EpoxyController
|
import com.airbnb.epoxy.EpoxyController
|
||||||
import com.airbnb.epoxy.EpoxyModel
|
import com.airbnb.epoxy.EpoxyModel
|
||||||
import com.airbnb.epoxy.VisibilityState
|
import com.airbnb.epoxy.VisibilityState
|
||||||
|
import im.vector.matrix.android.api.session.Session
|
||||||
import im.vector.matrix.android.api.session.room.model.message.*
|
import im.vector.matrix.android.api.session.room.model.message.*
|
||||||
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
|
@ -39,9 +40,11 @@ import im.vector.riotx.features.home.room.detail.timeline.item.*
|
||||||
import im.vector.riotx.features.media.ImageContentRenderer
|
import im.vector.riotx.features.media.ImageContentRenderer
|
||||||
import im.vector.riotx.features.media.VideoContentRenderer
|
import im.vector.riotx.features.media.VideoContentRenderer
|
||||||
import org.threeten.bp.LocalDateTime
|
import org.threeten.bp.LocalDateTime
|
||||||
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class TimelineEventController @Inject constructor(private val dateFormatter: VectorDateFormatter,
|
class TimelineEventController @Inject constructor(private val dateFormatter: VectorDateFormatter,
|
||||||
|
private val session: Session,
|
||||||
private val timelineItemFactory: TimelineItemFactory,
|
private val timelineItemFactory: TimelineItemFactory,
|
||||||
private val timelineMediaSizeProvider: TimelineMediaSizeProvider,
|
private val timelineMediaSizeProvider: TimelineMediaSizeProvider,
|
||||||
private val mergedHeaderItemFactory: MergedHeaderItemFactory,
|
private val mergedHeaderItemFactory: MergedHeaderItemFactory,
|
||||||
|
@ -239,21 +242,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getModels(): List<EpoxyModel<*>> {
|
private fun getModels(): List<EpoxyModel<*>> {
|
||||||
synchronized(modelCache) {
|
buildCacheItemsIfNeeded()
|
||||||
val readMarkerIdSnapshot = this.readMarkerIdSnapshot
|
|
||||||
val displayableReadMarkerId = if (readMarkerIdSnapshot != null) {
|
|
||||||
timeline?.getFirstDisplayableEventId(readMarkerIdSnapshot)
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
(0 until modelCache.size).forEach { position ->
|
|
||||||
// Should be build if not cached or if cached but contains additional models
|
|
||||||
// We then are sure we always have items up to date.
|
|
||||||
if (modelCache[position] == null || modelCache[position]?.hasAdditionalModel() == true) {
|
|
||||||
modelCache[position] = buildItemModels(position, currentSnapshot, displayableReadMarkerId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return modelCache
|
return modelCache
|
||||||
.map {
|
.map {
|
||||||
val eventModel = if (it == null || mergedHeaderItemFactory.isCollapsed(it.localId)) {
|
val eventModel = if (it == null || mergedHeaderItemFactory.isCollapsed(it.localId)) {
|
||||||
|
@ -261,13 +250,41 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
|
||||||
} else {
|
} else {
|
||||||
it.eventModel
|
it.eventModel
|
||||||
}
|
}
|
||||||
listOf(it?.readMarkerModel, eventModel, it?.mergedHeaderModel, it?.formattedDayModel)
|
listOf(eventModel, it?.mergedHeaderModel, it?.formattedDayModel, it?.readMarkerModel)
|
||||||
}
|
}
|
||||||
.flatten()
|
.flatten()
|
||||||
.filterNotNull()
|
.filterNotNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildItemModels(currentPosition: Int, items: List<TimelineEvent>, displayableReadMarkerId: String?): CacheItemData {
|
private fun buildCacheItemsIfNeeded() = synchronized(modelCache) {
|
||||||
|
if (modelCache.isEmpty()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val displayableReadMarkerId = computeDisplayableReadMarkerId()
|
||||||
|
(0 until modelCache.size).forEach { position ->
|
||||||
|
// Should be build if not cached or if cached but contains additional models
|
||||||
|
// We then are sure we always have items up to date.
|
||||||
|
if (modelCache[position] == null || modelCache[position]?.shouldTriggerBuild() == true) {
|
||||||
|
modelCache[position] = buildCacheItem(position, currentSnapshot, displayableReadMarkerId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun computeDisplayableReadMarkerId(): String? {
|
||||||
|
val readMarkerIdSnapshot = this.readMarkerIdSnapshot ?: return null
|
||||||
|
val firstDisplayableEventId = timeline?.getFirstDisplayableEventId(readMarkerIdSnapshot) ?: return null
|
||||||
|
val firstDisplayableEventIndex = timeline?.getIndexOfEvent(firstDisplayableEventId) ?: return null
|
||||||
|
for (i in (firstDisplayableEventIndex - 1) downTo 0) {
|
||||||
|
val timelineEvent = currentSnapshot.getOrNull(i) ?: return null
|
||||||
|
val isFromMe = timelineEvent.root.senderId == session.myUserId
|
||||||
|
if (!isFromMe) {
|
||||||
|
return timelineEvent.root.eventId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildCacheItem(currentPosition: Int, items: List<TimelineEvent>, displayableReadMarkerId: String?): CacheItemData {
|
||||||
val event = items[currentPosition]
|
val event = items[currentPosition]
|
||||||
val nextEvent = items.nextOrNull(currentPosition)
|
val nextEvent = items.nextOrNull(currentPosition)
|
||||||
val date = event.root.localDateTime()
|
val date = event.root.localDateTime()
|
||||||
|
@ -288,15 +305,15 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
|
||||||
requestModelBuild()
|
requestModelBuild()
|
||||||
}
|
}
|
||||||
val daySeparatorItem = buildDaySeparatorItem(addDaySeparator, date)
|
val daySeparatorItem = buildDaySeparatorItem(addDaySeparator, date)
|
||||||
val readMarkerItem = buildReadMarkerItem(currentPosition, event, displayableReadMarkerId)
|
val readMarkerItem = buildReadMarkerItem(event, displayableReadMarkerId)
|
||||||
return CacheItemData(event.localId, event.root.eventId, eventModel, mergedHeaderModel, daySeparatorItem, readMarkerItem)
|
return CacheItemData(event.localId, event.root.eventId, eventModel, mergedHeaderModel, daySeparatorItem, readMarkerItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildReadMarkerItem(currentPosition: Int, event: TimelineEvent, displayableReadMarkerId: String?): TimelineReadMarkerItem? {
|
private fun buildReadMarkerItem(event: TimelineEvent, displayableReadMarkerId: String?): TimelineReadMarkerItem? {
|
||||||
return if (currentPosition != 0 && event.root.eventId == displayableReadMarkerId) {
|
return if (event.root.eventId == displayableReadMarkerId) {
|
||||||
TimelineReadMarkerItem_()
|
TimelineReadMarkerItem_()
|
||||||
.also {
|
.also {
|
||||||
it.id(event.localId)
|
it.id("read_marker")
|
||||||
it.setOnVisibilityStateChanged(ReadMarkerVisibilityStateChangedListener(callback))
|
it.setOnVisibilityStateChanged(ReadMarkerVisibilityStateChangedListener(callback))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -347,6 +364,6 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
|
||||||
val formattedDayModel: DaySeparatorItem? = null,
|
val formattedDayModel: DaySeparatorItem? = null,
|
||||||
val readMarkerModel: TimelineReadMarkerItem? = null
|
val readMarkerModel: TimelineReadMarkerItem? = null
|
||||||
) {
|
) {
|
||||||
fun hasAdditionalModel() = mergedHeaderModel != null || formattedDayModel != null || readMarkerModel != null
|
fun shouldTriggerBuild() = mergedHeaderModel != null || formattedDayModel != null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="4dp"
|
android:layout_marginTop="8dp"
|
||||||
android:layout_marginBottom="4dp"
|
android:layout_marginBottom="8dp"
|
||||||
android:background="?attr/riotx_header_panel_background"
|
android:background="?attr/riotx_header_panel_background"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:minHeight="32dp"
|
android:minHeight="32dp"
|
||||||
|
|
Loading…
Reference in a new issue