Timeline: refact [WIP]
This commit is contained in:
parent
63b43de4b8
commit
4a80df082c
@ -38,10 +38,12 @@ internal fun EventAnnotationsSummaryEntity.Companion.whereInRoom(realm: Realm, r
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
internal fun EventAnnotationsSummaryEntity.Companion.create(realm: Realm, eventId: String): EventAnnotationsSummaryEntity {
|
internal fun EventAnnotationsSummaryEntity.Companion.create(realm: Realm, roomId: String, eventId: String): EventAnnotationsSummaryEntity {
|
||||||
val obj = realm.createObject(EventAnnotationsSummaryEntity::class.java, eventId)
|
val obj = realm.createObject(EventAnnotationsSummaryEntity::class.java, eventId).apply {
|
||||||
|
this.roomId = roomId
|
||||||
|
}
|
||||||
//Denormalization
|
//Denormalization
|
||||||
TimelineEventEntity.where(realm, eventId = eventId).findFirst()?.let {
|
TimelineEventEntity.where(realm, roomId = roomId, eventId = eventId).findFirst()?.let {
|
||||||
it.annotations = obj
|
it.annotations = obj
|
||||||
}
|
}
|
||||||
return obj
|
return obj
|
||||||
|
@ -27,10 +27,7 @@ internal fun ReadReceiptsSummaryEntity.Companion.where(realm: Realm, eventId: St
|
|||||||
.equalTo(ReadReceiptsSummaryEntityFields.EVENT_ID, eventId)
|
.equalTo(ReadReceiptsSummaryEntityFields.EVENT_ID, eventId)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun ReadReceiptsSummaryEntity.Companion.whereInRoom(realm: Realm, roomId: String?): RealmQuery<ReadReceiptsSummaryEntity> {
|
internal fun ReadReceiptsSummaryEntity.Companion.whereInRoom(realm: Realm, roomId: String): RealmQuery<ReadReceiptsSummaryEntity> {
|
||||||
val query = realm.where<ReadReceiptsSummaryEntity>()
|
return realm.where<ReadReceiptsSummaryEntity>()
|
||||||
if (roomId != null) {
|
.equalTo(ReadReceiptsSummaryEntityFields.ROOM_ID, roomId)
|
||||||
query.equalTo(ReadReceiptsSummaryEntityFields.ROOM_ID, roomId)
|
|
||||||
}
|
|
||||||
return query
|
|
||||||
}
|
}
|
||||||
|
@ -22,13 +22,15 @@ import im.vector.matrix.android.internal.database.model.EventEntity.LinkFilterMo
|
|||||||
import io.realm.*
|
import io.realm.*
|
||||||
import io.realm.kotlin.where
|
import io.realm.kotlin.where
|
||||||
|
|
||||||
internal fun TimelineEventEntity.Companion.where(realm: Realm, eventId: String): RealmQuery<TimelineEventEntity> {
|
internal fun TimelineEventEntity.Companion.where(realm: Realm, roomId: String, eventId: String): RealmQuery<TimelineEventEntity> {
|
||||||
return realm.where<TimelineEventEntity>()
|
return realm.where<TimelineEventEntity>()
|
||||||
|
.equalTo(TimelineEventEntityFields.ROOM_ID, roomId)
|
||||||
.equalTo(TimelineEventEntityFields.EVENT_ID, eventId)
|
.equalTo(TimelineEventEntityFields.EVENT_ID, eventId)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun TimelineEventEntity.Companion.where(realm: Realm, eventIds: List<String>): RealmQuery<TimelineEventEntity> {
|
internal fun TimelineEventEntity.Companion.where(realm: Realm, roomId: String, eventIds: List<String>): RealmQuery<TimelineEventEntity> {
|
||||||
return realm.where<TimelineEventEntity>()
|
return realm.where<TimelineEventEntity>()
|
||||||
|
.equalTo(TimelineEventEntityFields.ROOM_ID, roomId)
|
||||||
.`in`(TimelineEventEntityFields.EVENT_ID, eventIds.toTypedArray())
|
.`in`(TimelineEventEntityFields.EVENT_ID, eventIds.toTypedArray())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ internal class DefaultEventRelationsAggregationTask @Inject constructor(
|
|||||||
|
|
||||||
EventAnnotationsSummaryEntity.where(realm, event.eventId
|
EventAnnotationsSummaryEntity.where(realm, event.eventId
|
||||||
?: "").findFirst()?.let {
|
?: "").findFirst()?.let {
|
||||||
TimelineEventEntity.where(realm, eventId = event.eventId
|
TimelineEventEntity.where(realm, roomId = roomId, eventId = event.eventId
|
||||||
?: "").findFirst()?.let { tet ->
|
?: "").findFirst()?.let { tet ->
|
||||||
tet.annotations = it
|
tet.annotations = it
|
||||||
}
|
}
|
||||||
@ -167,8 +167,7 @@ internal class DefaultEventRelationsAggregationTask @Inject constructor(
|
|||||||
var existing = EventAnnotationsSummaryEntity.where(realm, targetEventId).findFirst()
|
var existing = EventAnnotationsSummaryEntity.where(realm, targetEventId).findFirst()
|
||||||
if (existing == null) {
|
if (existing == null) {
|
||||||
Timber.v("###REPLACE creating new relation summary for $targetEventId")
|
Timber.v("###REPLACE creating new relation summary for $targetEventId")
|
||||||
existing = EventAnnotationsSummaryEntity.create(realm, targetEventId)
|
existing = EventAnnotationsSummaryEntity.create(realm, roomId, targetEventId)
|
||||||
existing.roomId = roomId
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//we have it
|
//we have it
|
||||||
@ -233,8 +232,7 @@ internal class DefaultEventRelationsAggregationTask @Inject constructor(
|
|||||||
val eventId = event.eventId ?: ""
|
val eventId = event.eventId ?: ""
|
||||||
val existing = EventAnnotationsSummaryEntity.where(realm, eventId).findFirst()
|
val existing = EventAnnotationsSummaryEntity.where(realm, eventId).findFirst()
|
||||||
if (existing == null) {
|
if (existing == null) {
|
||||||
val eventSummary = EventAnnotationsSummaryEntity.create(realm, eventId)
|
val eventSummary = EventAnnotationsSummaryEntity.create(realm, roomId, eventId)
|
||||||
eventSummary.roomId = roomId
|
|
||||||
val sum = realm.createObject(ReactionAggregatedSummaryEntity::class.java)
|
val sum = realm.createObject(ReactionAggregatedSummaryEntity::class.java)
|
||||||
sum.key = it.key
|
sum.key = it.key
|
||||||
sum.firstTimestamp = event.originServerTs ?: 0 //TODO how to maintain order?
|
sum.firstTimestamp = event.originServerTs ?: 0 //TODO how to maintain order?
|
||||||
@ -261,7 +259,7 @@ internal class DefaultEventRelationsAggregationTask @Inject constructor(
|
|||||||
val reactionEventId = event.eventId
|
val reactionEventId = event.eventId
|
||||||
Timber.v("Reaction $reactionEventId relates to $relatedEventID")
|
Timber.v("Reaction $reactionEventId relates to $relatedEventID")
|
||||||
val eventSummary = EventAnnotationsSummaryEntity.where(realm, relatedEventID).findFirst()
|
val eventSummary = EventAnnotationsSummaryEntity.where(realm, relatedEventID).findFirst()
|
||||||
?: EventAnnotationsSummaryEntity.create(realm, relatedEventID).apply { this.roomId = roomId }
|
?: EventAnnotationsSummaryEntity.create(realm, roomId, relatedEventID).apply { this.roomId = roomId }
|
||||||
|
|
||||||
var sum = eventSummary.reactionsSummary.find { it.key == reaction }
|
var sum = eventSummary.reactionsSummary.find { it.key == reaction }
|
||||||
val txId = event.unsignedData?.transactionId
|
val txId = event.unsignedData?.transactionId
|
||||||
|
@ -17,9 +17,7 @@
|
|||||||
package im.vector.matrix.android.internal.session.room.read
|
package im.vector.matrix.android.internal.session.room.read
|
||||||
|
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import im.vector.matrix.android.internal.database.model.ChunkEntity
|
|
||||||
import im.vector.matrix.android.internal.database.model.ReadMarkerEntity
|
import im.vector.matrix.android.internal.database.model.ReadMarkerEntity
|
||||||
import im.vector.matrix.android.internal.database.model.ReadReceiptEntity
|
|
||||||
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
|
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
|
||||||
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
|
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
|
||||||
import im.vector.matrix.android.internal.database.query.*
|
import im.vector.matrix.android.internal.database.query.*
|
||||||
@ -121,14 +119,15 @@ internal class DefaultSetReadMarkersTask @Inject constructor(private val roomAPI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isReadMarkerMoreRecent(roomId: String, fullyReadEventId: String): Boolean {
|
private fun isReadMarkerMoreRecent(roomId: String, newReadMarkerId: String): Boolean {
|
||||||
return Realm.getInstance(monarchy.realmConfiguration).use { realm ->
|
return Realm.getInstance(monarchy.realmConfiguration).use { realm ->
|
||||||
val readMarkerEntity = ReadMarkerEntity.where(realm, roomId = roomId).findFirst()
|
val currentReadMarkerId = ReadMarkerEntity.where(realm, roomId = roomId).findFirst()?.eventId
|
||||||
val readMarkerEvent = readMarkerEntity?.timelineEvent?.firstOrNull()
|
?: return true
|
||||||
val eventToCheck = TimelineEventEntity.where(realm, eventId = fullyReadEventId).findFirst()
|
val readMarkerEvent = TimelineEventEntity.where(realm, roomId = roomId, eventId = currentReadMarkerId).findFirst()
|
||||||
val readReceiptIndex = readMarkerEvent?.root?.displayIndex ?: Int.MAX_VALUE
|
val newReadMarkerEvent = TimelineEventEntity.where(realm, roomId = roomId, eventId = newReadMarkerId).findFirst()
|
||||||
val eventToCheckIndex = eventToCheck?.root?.displayIndex ?: Int.MIN_VALUE
|
val currentReadMarkerIndex = readMarkerEvent?.root?.displayIndex ?: Int.MAX_VALUE
|
||||||
eventToCheckIndex > readReceiptIndex
|
val newReadMarkerIndex = newReadMarkerEvent?.root?.displayIndex ?: Int.MIN_VALUE
|
||||||
|
newReadMarkerIndex > currentReadMarkerIndex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,7 +158,7 @@ internal class DefaultSendService @AssistedInject constructor(@Assisted private
|
|||||||
|
|
||||||
override fun deleteFailedEcho(localEcho: TimelineEvent) {
|
override fun deleteFailedEcho(localEcho: TimelineEvent) {
|
||||||
monarchy.writeAsync { realm ->
|
monarchy.writeAsync { realm ->
|
||||||
TimelineEventEntity.where(realm, eventId = localEcho.root.eventId ?: "").findFirst()?.let {
|
TimelineEventEntity.where(realm, roomId = roomId, eventId = localEcho.root.eventId ?: "").findFirst()?.let {
|
||||||
it.deleteFromRealm()
|
it.deleteFromRealm()
|
||||||
}
|
}
|
||||||
EventEntity.where(realm, eventId = localEcho.root.eventId ?: "").findFirst()?.let {
|
EventEntity.where(realm, eventId = localEcho.root.eventId ?: "").findFirst()?.let {
|
||||||
|
@ -24,6 +24,7 @@ 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
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineSettings
|
import im.vector.matrix.android.api.session.room.timeline.TimelineSettings
|
||||||
import im.vector.matrix.android.api.util.CancelableBag
|
import im.vector.matrix.android.api.util.CancelableBag
|
||||||
|
import im.vector.matrix.android.internal.database.helper.deleteOnCascade
|
||||||
import im.vector.matrix.android.internal.database.mapper.TimelineEventMapper
|
import im.vector.matrix.android.internal.database.mapper.TimelineEventMapper
|
||||||
import im.vector.matrix.android.internal.database.mapper.asDomain
|
import im.vector.matrix.android.internal.database.mapper.asDomain
|
||||||
import im.vector.matrix.android.internal.database.model.ChunkEntity
|
import im.vector.matrix.android.internal.database.model.ChunkEntity
|
||||||
@ -625,12 +626,14 @@ internal class DefaultTimeline(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun clearUnlinkedEvents(realm: Realm) {
|
private fun clearUnlinkedEvents(realm: Realm) {
|
||||||
realm.executeTransaction {
|
realm.executeTransaction { localRealm ->
|
||||||
val unlinkedChunks = ChunkEntity
|
val unlinkedChunks = ChunkEntity
|
||||||
.where(it, roomId = roomId)
|
.where(localRealm, roomId = roomId)
|
||||||
.equalTo("${ChunkEntityFields.TIMELINE_EVENTS.ROOT}.${EventEntityFields.IS_UNLINKED}", true)
|
.equalTo("${ChunkEntityFields.TIMELINE_EVENTS.ROOT}.${EventEntityFields.IS_UNLINKED}", true)
|
||||||
.findAll()
|
.findAll()
|
||||||
unlinkedChunks.deleteAllFromRealm()
|
unlinkedChunks.forEach {
|
||||||
|
it.deleteOnCascade()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,14 +60,14 @@ internal class DefaultTimelineService @AssistedInject constructor(@Assisted priv
|
|||||||
timelineEventMapper,
|
timelineEventMapper,
|
||||||
settings,
|
settings,
|
||||||
TimelineHiddenReadReceipts(readReceiptsSummaryMapper, roomId, settings),
|
TimelineHiddenReadReceipts(readReceiptsSummaryMapper, roomId, settings),
|
||||||
TimelineHiddenReadMarker(roomId)
|
TimelineHiddenReadMarker(roomId, settings)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getTimeLineEvent(eventId: String): TimelineEvent? {
|
override fun getTimeLineEvent(eventId: String): TimelineEvent? {
|
||||||
return monarchy
|
return monarchy
|
||||||
.fetchCopyMap({
|
.fetchCopyMap({
|
||||||
TimelineEventEntity.where(it, eventId = eventId).findFirst()
|
TimelineEventEntity.where(it, roomId = roomId, eventId = eventId).findFirst()
|
||||||
}, { entity, realm ->
|
}, { entity, realm ->
|
||||||
timelineEventMapper.map(entity)
|
timelineEventMapper.map(entity)
|
||||||
})
|
})
|
||||||
@ -75,7 +75,7 @@ internal class DefaultTimelineService @AssistedInject constructor(@Assisted priv
|
|||||||
|
|
||||||
override fun getTimeLineEventLive(eventId: String): LiveData<TimelineEvent> {
|
override fun getTimeLineEventLive(eventId: String): LiveData<TimelineEvent> {
|
||||||
val liveData = RealmLiveData(monarchy.realmConfiguration) {
|
val liveData = RealmLiveData(monarchy.realmConfiguration) {
|
||||||
TimelineEventEntity.where(it, eventId = eventId)
|
TimelineEventEntity.where(it, roomId = roomId, eventId = eventId)
|
||||||
}
|
}
|
||||||
return Transformations.map(liveData) { events ->
|
return Transformations.map(liveData) { events ->
|
||||||
events.firstOrNull()?.let { timelineEventMapper.map(it) }
|
events.firstOrNull()?.let { timelineEventMapper.map(it) }
|
||||||
|
@ -18,12 +18,16 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.session.room.timeline
|
package im.vector.matrix.android.internal.session.room.timeline
|
||||||
|
|
||||||
|
import im.vector.matrix.android.api.session.room.timeline.TimelineSettings
|
||||||
import im.vector.matrix.android.internal.database.model.ReadMarkerEntity
|
import im.vector.matrix.android.internal.database.model.ReadMarkerEntity
|
||||||
|
import im.vector.matrix.android.internal.database.model.ReadMarkerEntityFields
|
||||||
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
|
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
|
||||||
import im.vector.matrix.android.internal.database.model.TimelineEventEntityFields
|
import im.vector.matrix.android.internal.database.model.TimelineEventEntityFields
|
||||||
|
import im.vector.matrix.android.internal.database.query.FilterContent
|
||||||
import im.vector.matrix.android.internal.database.query.where
|
import im.vector.matrix.android.internal.database.query.where
|
||||||
|
import io.realm.OrderedRealmCollectionChangeListener
|
||||||
import io.realm.Realm
|
import io.realm.Realm
|
||||||
import io.realm.RealmObjectChangeListener
|
import io.realm.RealmQuery
|
||||||
import io.realm.RealmResults
|
import io.realm.RealmResults
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -31,7 +35,8 @@ import io.realm.RealmResults
|
|||||||
* When an hidden event has read marker, we want to transfer it on the first older displayed event.
|
* When an hidden event has read marker, we want to transfer it on the first older displayed event.
|
||||||
* It has to be used in [DefaultTimeline] and we should call the [start] and [dispose] methods to properly handle realm subscription.
|
* It has to be used in [DefaultTimeline] and we should call the [start] and [dispose] methods to properly handle realm subscription.
|
||||||
*/
|
*/
|
||||||
internal class TimelineHiddenReadMarker constructor(private val roomId: String) {
|
internal class TimelineHiddenReadMarker constructor(private val roomId: String,
|
||||||
|
private val settings: TimelineSettings) {
|
||||||
|
|
||||||
interface Delegate {
|
interface Delegate {
|
||||||
fun rebuildEvent(eventId: String, hasReadMarker: Boolean): Boolean
|
fun rebuildEvent(eventId: String, hasReadMarker: Boolean): Boolean
|
||||||
@ -39,24 +44,26 @@ internal class TimelineHiddenReadMarker constructor(private val roomId: String)
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var previousDisplayedEventId: String? = null
|
private var previousDisplayedEventId: String? = null
|
||||||
private var readMarkerEntity: ReadMarkerEntity? = null
|
private var hiddenReadMarker: RealmResults<ReadMarkerEntity>? = null
|
||||||
|
|
||||||
private lateinit var liveEvents: RealmResults<TimelineEventEntity>
|
private lateinit var liveEvents: RealmResults<TimelineEventEntity>
|
||||||
private lateinit var delegate: Delegate
|
private lateinit var delegate: Delegate
|
||||||
|
|
||||||
private val readMarkerListener = RealmObjectChangeListener<ReadMarkerEntity> { readMarker, _ ->
|
private val readMarkerListener = OrderedRealmCollectionChangeListener<RealmResults<ReadMarkerEntity>> { readMarkers, changeSet ->
|
||||||
if (!readMarker.isLoaded || !readMarker.isValid) {
|
if (!readMarkers.isLoaded || !readMarkers.isValid) {
|
||||||
return@RealmObjectChangeListener
|
return@OrderedRealmCollectionChangeListener
|
||||||
}
|
}
|
||||||
var hasChange = false
|
var hasChange = false
|
||||||
|
if (changeSet.deletions.isNotEmpty()) {
|
||||||
previousDisplayedEventId?.also {
|
previousDisplayedEventId?.also {
|
||||||
hasChange = delegate.rebuildEvent(it, false)
|
hasChange = delegate.rebuildEvent(it, false)
|
||||||
previousDisplayedEventId = null
|
previousDisplayedEventId = null
|
||||||
}
|
}
|
||||||
val isEventHidden = liveEvents.where().equalTo(TimelineEventEntityFields.EVENT_ID, readMarker.eventId).findFirst() == null
|
}
|
||||||
if (isEventHidden) {
|
val readMarker = readMarkers.firstOrNull() ?: return@OrderedRealmCollectionChangeListener
|
||||||
val hiddenEvent = readMarker.timelineEvent?.firstOrNull()
|
val hiddenEvent = readMarker.timelineEvent?.firstOrNull()
|
||||||
?: return@RealmObjectChangeListener
|
?: return@OrderedRealmCollectionChangeListener
|
||||||
|
|
||||||
val displayIndex = hiddenEvent.root?.displayIndex
|
val displayIndex = hiddenEvent.root?.displayIndex
|
||||||
if (displayIndex != null) {
|
if (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
|
||||||
@ -70,8 +77,9 @@ internal class TimelineHiddenReadMarker constructor(private val roomId: String)
|
|||||||
hasChange = delegate.rebuildEvent(firstDisplayedEvent.eventId, true)
|
hasChange = delegate.rebuildEvent(firstDisplayedEvent.eventId, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (hasChange) {
|
||||||
|
delegate.onReadMarkerUpdated()
|
||||||
}
|
}
|
||||||
if (hasChange) delegate.onReadMarkerUpdated()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -83,8 +91,10 @@ internal class TimelineHiddenReadMarker constructor(private val roomId: String)
|
|||||||
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).
|
||||||
readMarkerEntity = ReadMarkerEntity.where(realm, roomId = roomId)
|
hiddenReadMarker = ReadMarkerEntity.where(realm, roomId = roomId)
|
||||||
.findFirstAsync()
|
.isNotEmpty(ReadMarkerEntityFields.TIMELINE_EVENT)
|
||||||
|
.filterReceiptsWithSettings()
|
||||||
|
.findAllAsync()
|
||||||
.also { it.addChangeListener(readMarkerListener) }
|
.also { it.addChangeListener(readMarkerListener) }
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -93,7 +103,26 @@ internal class TimelineHiddenReadMarker constructor(private val roomId: String)
|
|||||||
* Dispose the realm query subscription. Has to be called on an HandlerThread
|
* Dispose the realm query subscription. Has to be called on an HandlerThread
|
||||||
*/
|
*/
|
||||||
fun dispose() {
|
fun dispose() {
|
||||||
this.readMarkerEntity?.removeAllChangeListeners()
|
this.hiddenReadMarker?.removeAllChangeListeners()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We are looking for readMarker related to filtered events. So, it's the opposite of [DefaultTimeline.filterEventsWithSettings] method.
|
||||||
|
*/
|
||||||
|
private fun RealmQuery<ReadMarkerEntity>.filterReceiptsWithSettings(): RealmQuery<ReadMarkerEntity> {
|
||||||
|
beginGroup()
|
||||||
|
if (settings.filterTypes) {
|
||||||
|
not().`in`("${ReadMarkerEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.TYPE}", settings.allowedTypes.toTypedArray())
|
||||||
|
}
|
||||||
|
if (settings.filterTypes && settings.filterEdits) {
|
||||||
|
or()
|
||||||
|
}
|
||||||
|
if (settings.filterEdits) {
|
||||||
|
like("${ReadMarkerEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.CONTENT}", FilterContent.EDIT_TYPE)
|
||||||
|
}
|
||||||
|
endGroup()
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -16,10 +16,12 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.session.sync
|
package im.vector.matrix.android.internal.session.sync
|
||||||
|
|
||||||
|
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||||
import im.vector.matrix.android.internal.session.room.read.FullyReadContent
|
import im.vector.matrix.android.internal.session.room.read.FullyReadContent
|
||||||
import im.vector.matrix.android.internal.database.model.ReadMarkerEntity
|
import im.vector.matrix.android.internal.database.model.ReadMarkerEntity
|
||||||
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
|
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
|
||||||
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
|
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
|
||||||
|
import im.vector.matrix.android.internal.database.model.TimelineEventEntityFields
|
||||||
import im.vector.matrix.android.internal.database.query.getOrCreate
|
import im.vector.matrix.android.internal.database.query.getOrCreate
|
||||||
import im.vector.matrix.android.internal.database.query.where
|
import im.vector.matrix.android.internal.database.query.where
|
||||||
import io.realm.Realm
|
import io.realm.Realm
|
||||||
@ -37,15 +39,14 @@ internal class RoomFullyReadHandler @Inject constructor() {
|
|||||||
RoomSummaryEntity.getOrCreate(realm, roomId).apply {
|
RoomSummaryEntity.getOrCreate(realm, roomId).apply {
|
||||||
readMarkerId = content.eventId
|
readMarkerId = content.eventId
|
||||||
}
|
}
|
||||||
val readMarkerEntity = ReadMarkerEntity.getOrCreate(realm, roomId)
|
// Remove the old markers if any
|
||||||
// Remove the old marker if any
|
val oldReadMarkerEvents = TimelineEventEntity.where(realm, roomId = roomId, linkFilterMode = EventEntity.LinkFilterMode.BOTH).isNotNull(TimelineEventEntityFields.READ_MARKER.`$`).findAll()
|
||||||
if (readMarkerEntity.eventId.isNotEmpty()) {
|
oldReadMarkerEvents.forEach { it.readMarker = null }
|
||||||
val oldReadMarkerEvent = TimelineEventEntity.where(realm, eventId = readMarkerEntity.eventId).findFirst()
|
val readMarkerEntity = ReadMarkerEntity.getOrCreate(realm, roomId).apply {
|
||||||
oldReadMarkerEvent?.readMarker = null
|
this.eventId = content.eventId
|
||||||
}
|
}
|
||||||
readMarkerEntity.eventId = content.eventId
|
|
||||||
// Attach to timelineEvent if known
|
// Attach to timelineEvent if known
|
||||||
val timelineEventEntity = TimelineEventEntity.where(realm, eventId = content.eventId).findFirst()
|
val timelineEventEntity = TimelineEventEntity.where(realm, roomId = roomId, eventId = content.eventId).findFirst()
|
||||||
timelineEventEntity?.readMarker = readMarkerEntity
|
timelineEventEntity?.readMarker = readMarkerEntity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,16 +21,21 @@ package im.vector.riotx.core.ui.views
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.RelativeLayout
|
import android.widget.RelativeLayout
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.core.view.updateLayoutParams
|
||||||
import butterknife.ButterKnife
|
import butterknife.ButterKnife
|
||||||
|
import com.airbnb.epoxy.VisibilityState
|
||||||
|
import com.google.android.material.internal.ViewUtils.dpToPx
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.features.themes.ThemeUtils
|
import im.vector.riotx.features.themes.ThemeUtils
|
||||||
import kotlinx.android.synthetic.main.view_jump_to_read_marker.view.*
|
import kotlinx.android.synthetic.main.view_jump_to_read_marker.view.*
|
||||||
import me.gujun.android.span.span
|
import me.gujun.android.span.span
|
||||||
import me.saket.bettermovementmethod.BetterLinkMovementMethod
|
import me.saket.bettermovementmethod.BetterLinkMovementMethod
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
class JumpToReadMarkerView @JvmOverloads constructor(
|
class JumpToReadMarkerView @JvmOverloads constructor(
|
||||||
context: Context,
|
context: Context,
|
||||||
@ -49,27 +54,35 @@ class JumpToReadMarkerView @JvmOverloads constructor(
|
|||||||
setupView()
|
setupView()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var readMarkerId: String? = null
|
||||||
|
|
||||||
private fun setupView() {
|
private fun setupView() {
|
||||||
LinearLayout.inflate(context, R.layout.view_jump_to_read_marker, this)
|
inflate(context, R.layout.view_jump_to_read_marker, this)
|
||||||
setBackgroundColor(ContextCompat.getColor(context, R.color.notification_accent_color))
|
setBackgroundColor(ContextCompat.getColor(context, R.color.notification_accent_color))
|
||||||
jumpToReadMarkerLabelView.movementMethod = BetterLinkMovementMethod.getInstance()
|
jumpToReadMarkerLabelView.movementMethod = BetterLinkMovementMethod.getInstance()
|
||||||
isClickable = true
|
isClickable = true
|
||||||
|
jumpToReadMarkerLabelView.text = span(resources.getString(R.string.room_jump_to_first_unread)) {
|
||||||
|
textDecorationLine = "underline"
|
||||||
|
onClick = {
|
||||||
|
readMarkerId?.also {
|
||||||
|
callback?.onJumpToReadMarkerClicked(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
closeJumpToReadMarkerView.setOnClickListener {
|
closeJumpToReadMarkerView.setOnClickListener {
|
||||||
visibility = View.GONE
|
visibility = View.INVISIBLE
|
||||||
callback?.onClearReadMarkerClicked()
|
callback?.onClearReadMarkerClicked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun render(show: Boolean, readMarkerId: String?) {
|
fun render(show: Boolean, readMarkerId: String?) {
|
||||||
isVisible = show
|
this.readMarkerId = readMarkerId
|
||||||
if (readMarkerId != null) {
|
visibility = if(show){
|
||||||
jumpToReadMarkerLabelView.text = span(resources.getString(R.string.room_jump_to_first_unread)) {
|
View.VISIBLE
|
||||||
textDecorationLine = "underline"
|
}else {
|
||||||
onClick = { callback?.onJumpToReadMarkerClicked(readMarkerId) }
|
View.INVISIBLE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,6 @@ class ReadMarkerView @JvmOverloads constructor(
|
|||||||
private var callbackDispatcherJob: Job? = null
|
private var callbackDispatcherJob: Job? = null
|
||||||
|
|
||||||
fun bindView(eventId: String?, hasReadMarker: Boolean, displayReadMarker: Boolean, readMarkerCallback: Callback) {
|
fun bindView(eventId: String?, hasReadMarker: Boolean, displayReadMarker: Boolean, readMarkerCallback: Callback) {
|
||||||
Timber.v("Bind event $eventId - hasReadMarker: $hasReadMarker - displayReadMarker: $displayReadMarker")
|
|
||||||
this.eventId = eventId
|
this.eventId = eventId
|
||||||
this.callback = readMarkerCallback
|
this.callback = readMarkerCallback
|
||||||
if (displayReadMarker) {
|
if (displayReadMarker) {
|
||||||
|
@ -19,6 +19,7 @@ package im.vector.riotx.features.home.room.detail
|
|||||||
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import im.vector.riotx.core.di.ScreenScope
|
import im.vector.riotx.core.di.ScreenScope
|
||||||
|
import im.vector.riotx.core.utils.createBackgroundHandler
|
||||||
import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController
|
import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -31,6 +32,7 @@ class ReadMarkerHelper @Inject constructor() {
|
|||||||
var callback: Callback? = null
|
var callback: Callback? = null
|
||||||
|
|
||||||
private var onReadMarkerLongDisplayed = false
|
private var onReadMarkerLongDisplayed = false
|
||||||
|
private var jumpToReadMarkerVisible = false
|
||||||
private var readMarkerVisible: Boolean = true
|
private var readMarkerVisible: Boolean = true
|
||||||
private var state: RoomDetailViewState? = null
|
private var state: RoomDetailViewState? = null
|
||||||
|
|
||||||
@ -75,22 +77,19 @@ class ReadMarkerHelper @Inject constructor() {
|
|||||||
val nonNullState = this.state ?: return
|
val nonNullState = this.state ?: return
|
||||||
val lastVisibleItem = layoutManager.findLastVisibleItemPosition()
|
val lastVisibleItem = layoutManager.findLastVisibleItemPosition()
|
||||||
val readMarkerId = nonNullState.asyncRoomSummary()?.readMarkerId
|
val readMarkerId = nonNullState.asyncRoomSummary()?.readMarkerId
|
||||||
if (readMarkerId == null) {
|
val newJumpToReadMarkerVisible = if (readMarkerId == null) {
|
||||||
callback?.onJumpToReadMarkerVisibilityUpdate(false, null)
|
false
|
||||||
}
|
} else {
|
||||||
val positionOfReadMarker = timelineEventController.searchPositionOfEvent(readMarkerId)
|
val positionOfReadMarker = timelineEventController.searchPositionOfEvent(readMarkerId)
|
||||||
if (positionOfReadMarker == null) {
|
if (positionOfReadMarker == null) {
|
||||||
if (nonNullState.timeline?.isLive == true && lastVisibleItem > 0) {
|
nonNullState.timeline?.isLive == true && lastVisibleItem > 0
|
||||||
callback?.onJumpToReadMarkerVisibilityUpdate(true, readMarkerId)
|
|
||||||
} else {
|
} else {
|
||||||
callback?.onJumpToReadMarkerVisibilityUpdate(false, readMarkerId)
|
positionOfReadMarker > lastVisibleItem
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (positionOfReadMarker > lastVisibleItem) {
|
|
||||||
callback?.onJumpToReadMarkerVisibilityUpdate(true, readMarkerId)
|
|
||||||
} else {
|
|
||||||
callback?.onJumpToReadMarkerVisibilityUpdate(false, readMarkerId)
|
|
||||||
}
|
}
|
||||||
|
if (newJumpToReadMarkerVisible != jumpToReadMarkerVisible) {
|
||||||
|
jumpToReadMarkerVisible = newJumpToReadMarkerVisible
|
||||||
|
callback?.onJumpToReadMarkerVisibilityUpdate(jumpToReadMarkerVisible, readMarkerId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,7 +157,7 @@ class RoomDetailFragment :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**x
|
||||||
* Sanitize the display name.
|
* Sanitize the display name.
|
||||||
*
|
*
|
||||||
* @param displayName the display name to sanitize
|
* @param displayName the display name to sanitize
|
||||||
@ -948,12 +948,19 @@ class RoomDetailFragment :
|
|||||||
.show(requireActivity().supportFragmentManager, "DISPLAY_READ_RECEIPTS")
|
.show(requireActivity().supportFragmentManager, "DISPLAY_READ_RECEIPTS")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onReadMarkerLongBound(isDisplayed: Boolean) {
|
override fun onReadMarkerLongBound(readMarkerId: String, isDisplayed: Boolean) {
|
||||||
if (isDisplayed) {
|
|
||||||
readMarkerHelper.onReadMarkerLongDisplayed()
|
readMarkerHelper.onReadMarkerLongDisplayed()
|
||||||
|
val readMarkerIndex = timelineEventController.searchPositionOfEvent(readMarkerId) ?: return
|
||||||
|
val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition()
|
||||||
|
if (readMarkerIndex > lastVisibleItemPosition) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition()
|
||||||
|
val firstVisibleItem = timelineEventController.adapter.getModelAtPosition(firstVisibleItemPosition)
|
||||||
|
val nextReadMarkerId = when (firstVisibleItem) {
|
||||||
|
is BaseEventItem -> firstVisibleItem.getEventId()
|
||||||
|
else -> null
|
||||||
}
|
}
|
||||||
val firstVisibleItem = layoutManager.findFirstVisibleItemPosition()
|
|
||||||
val nextReadMarkerId = timelineEventController.searchEventIdAtPosition(firstVisibleItem)
|
|
||||||
if (nextReadMarkerId != null) {
|
if (nextReadMarkerId != null) {
|
||||||
roomDetailViewModel.process(RoomDetailActions.SetReadMarkerAction(nextReadMarkerId))
|
roomDetailViewModel.process(RoomDetailActions.SetReadMarkerAction(nextReadMarkerId))
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,6 @@ import im.vector.riotx.core.utils.LiveEvent
|
|||||||
import im.vector.riotx.core.utils.subscribeLogError
|
import im.vector.riotx.core.utils.subscribeLogError
|
||||||
import im.vector.riotx.features.command.CommandParser
|
import im.vector.riotx.features.command.CommandParser
|
||||||
import im.vector.riotx.features.command.ParsedCommand
|
import im.vector.riotx.features.command.ParsedCommand
|
||||||
import im.vector.riotx.features.home.room.detail.timeline.TimelineLayoutManagerHolder
|
|
||||||
import im.vector.riotx.features.home.room.detail.timeline.helper.TimelineDisplayableEvents
|
import im.vector.riotx.features.home.room.detail.timeline.helper.TimelineDisplayableEvents
|
||||||
import im.vector.riotx.features.settings.VectorPreferences
|
import im.vector.riotx.features.settings.VectorPreferences
|
||||||
import io.reactivex.Observable
|
import io.reactivex.Observable
|
||||||
@ -72,7 +71,6 @@ import java.util.concurrent.TimeUnit
|
|||||||
|
|
||||||
|
|
||||||
class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: RoomDetailViewState,
|
class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: RoomDetailViewState,
|
||||||
private val timelineLayoutManagerHolder: TimelineLayoutManagerHolder,
|
|
||||||
private val userPreferencesProvider: UserPreferencesProvider,
|
private val userPreferencesProvider: UserPreferencesProvider,
|
||||||
private val vectorPreferences: VectorPreferences,
|
private val vectorPreferences: VectorPreferences,
|
||||||
private val session: Session
|
private val session: Session
|
||||||
@ -117,8 +115,6 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||||||
observeEventDisplayedActions()
|
observeEventDisplayedActions()
|
||||||
observeSummaryState()
|
observeSummaryState()
|
||||||
observeDrafts()
|
observeDrafts()
|
||||||
observeReadMarkerVisibility()
|
|
||||||
observeOwnState()
|
|
||||||
room.rx().loadRoomMembersIfNeeded().subscribeLogError().disposeOnClear()
|
room.rx().loadRoomMembersIfNeeded().subscribeLogError().disposeOnClear()
|
||||||
timeline.start()
|
timeline.start()
|
||||||
setState { copy(timeline = this@RoomDetailViewModel.timeline) }
|
setState { copy(timeline = this@RoomDetailViewModel.timeline) }
|
||||||
@ -681,7 +677,15 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun handleSetReadMarkerAction(action: RoomDetailActions.SetReadMarkerAction) = withState { state ->
|
private fun handleSetReadMarkerAction(action: RoomDetailActions.SetReadMarkerAction) = withState { state ->
|
||||||
room.setReadMarker(action.eventId, callback = object : MatrixCallback<Unit> {})
|
var readMarkerId = action.eventId
|
||||||
|
val indexOfEvent = timeline.getIndexOfEvent(readMarkerId)
|
||||||
|
// force to set the read marker on the next event
|
||||||
|
if (indexOfEvent != null) {
|
||||||
|
timeline.getTimelineEventAtIndex(indexOfEvent - 1)?.root?.eventId?.also { eventIdOfNext ->
|
||||||
|
readMarkerId = eventIdOfNext
|
||||||
|
}
|
||||||
|
}
|
||||||
|
room.setReadMarker(readMarkerId, callback = object : MatrixCallback<Unit> {})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleMarkAllAsRead() {
|
private fun handleMarkAllAsRead() {
|
||||||
@ -724,22 +728,6 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observeReadMarkerVisibility() {
|
|
||||||
Observable
|
|
||||||
.combineLatest(
|
|
||||||
room.rx().liveReadMarker(),
|
|
||||||
room.rx().liveReadReceipt(),
|
|
||||||
BiFunction<Optional<String>, Optional<String>, Boolean> { readMarker, readReceipt ->
|
|
||||||
readMarker.getOrNull() != readReceipt.getOrNull()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.subscribe {
|
|
||||||
setState { copy(readMarkerVisible = it) }
|
|
||||||
}
|
|
||||||
.disposeOnClear()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
timeline.dispose()
|
timeline.dispose()
|
||||||
super.onCleared()
|
super.onCleared()
|
||||||
|
@ -52,8 +52,7 @@ data class RoomDetailViewState(
|
|||||||
val tombstoneEvent: Event? = null,
|
val tombstoneEvent: Event? = null,
|
||||||
val tombstoneEventHandling: Async<String> = Uninitialized,
|
val tombstoneEventHandling: Async<String> = Uninitialized,
|
||||||
val syncState: SyncState = SyncState.IDLE,
|
val syncState: SyncState = SyncState.IDLE,
|
||||||
val highlightedEventId: String? = null,
|
val highlightedEventId: String? = null
|
||||||
val readMarkerVisible: Boolean = false
|
|
||||||
) : MvRxState {
|
) : MvRxState {
|
||||||
|
|
||||||
constructor(args: RoomDetailArgs) : this(roomId = args.roomId, eventId = args.eventId)
|
constructor(args: RoomDetailArgs) : this(roomId = args.roomId, eventId = args.eventId)
|
||||||
|
@ -82,7 +82,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
|
|||||||
|
|
||||||
interface ReadReceiptsCallback {
|
interface ReadReceiptsCallback {
|
||||||
fun onReadReceiptsClicked(readReceipts: List<ReadReceiptData>)
|
fun onReadReceiptsClicked(readReceipts: List<ReadReceiptData>)
|
||||||
fun onReadMarkerLongBound(isDisplayed: Boolean)
|
fun onReadMarkerLongBound(readMarkerId: String, isDisplayed: Boolean)
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UrlClickCallback {
|
interface UrlClickCallback {
|
||||||
@ -258,7 +258,13 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
|
|||||||
val date = event.root.localDateTime()
|
val date = event.root.localDateTime()
|
||||||
val nextDate = nextEvent?.root?.localDateTime()
|
val nextDate = nextEvent?.root?.localDateTime()
|
||||||
val addDaySeparator = date.toLocalDate() != nextDate?.toLocalDate()
|
val addDaySeparator = date.toLocalDate() != nextDate?.toLocalDate()
|
||||||
val eventModel = timelineItemFactory.create(event, nextEvent, eventIdToHighlight, readMarkerVisible, callback).also {
|
// Don't show read marker if it's on first item
|
||||||
|
val showReadMarker = if (currentPosition == 0 && event.hasReadMarker) {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
readMarkerVisible
|
||||||
|
}
|
||||||
|
val eventModel = timelineItemFactory.create(event, nextEvent, eventIdToHighlight, showReadMarker, callback).also {
|
||||||
it.id(event.localId)
|
it.id(event.localId)
|
||||||
it.setOnVisibilityStateChanged(TimelineEventVisibilityStateChangedListener(callback, event))
|
it.setOnVisibilityStateChanged(TimelineEventVisibilityStateChangedListener(callback, event))
|
||||||
}
|
}
|
||||||
@ -317,40 +323,23 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
|
|||||||
realPosition++
|
realPosition++
|
||||||
}
|
}
|
||||||
for (i in 0 until modelCache.size) {
|
for (i in 0 until modelCache.size) {
|
||||||
val itemCache = modelCache[i]
|
val itemCache = modelCache[i] ?: continue
|
||||||
if (itemCache?.eventId == eventId) {
|
if (itemCache.eventId == eventId) {
|
||||||
return realPosition
|
return realPosition
|
||||||
}
|
}
|
||||||
if (itemCache?.eventModel != null) {
|
if (itemCache.eventModel != null && !mergedHeaderItemFactory.isCollapsed(itemCache.localId)) {
|
||||||
realPosition++
|
realPosition++
|
||||||
}
|
}
|
||||||
if (itemCache?.mergedHeaderModel != null) {
|
if (itemCache.mergedHeaderModel != null) {
|
||||||
realPosition++
|
realPosition++
|
||||||
}
|
}
|
||||||
if (itemCache?.formattedDayModel != null) {
|
if (itemCache.formattedDayModel != null) {
|
||||||
realPosition++
|
realPosition++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun searchEventIdAtPosition(position: Int): String? = synchronized(modelCache) {
|
|
||||||
var offsetValue = 0
|
|
||||||
for (i in 0 until position) {
|
|
||||||
val itemCache = modelCache[i]
|
|
||||||
if (itemCache?.eventModel == null) {
|
|
||||||
offsetValue--
|
|
||||||
}
|
|
||||||
if (itemCache?.mergedHeaderModel != null) {
|
|
||||||
offsetValue++
|
|
||||||
}
|
|
||||||
if (itemCache?.formattedDayModel != null) {
|
|
||||||
offsetValue++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return modelCache.getOrNull(position - offsetValue)?.eventId
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isLoadingForward() = showingForwardLoader
|
fun isLoadingForward() = showingForwardLoader
|
||||||
|
|
||||||
private data class CacheItemData(
|
private data class CacheItemData(
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
* Copyright 2019 New Vector Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
|
|
||||||
*/
|
|
||||||
package im.vector.riotx.features.home.room.detail.timeline
|
|
||||||
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import im.vector.riotx.core.di.ScreenScope
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@ScreenScope
|
|
||||||
class TimelineLayoutManagerHolder @Inject constructor() {
|
|
||||||
|
|
||||||
lateinit var layoutManager: LinearLayoutManager
|
|
||||||
|
|
||||||
}
|
|
@ -71,7 +71,8 @@ class MergedHeaderItemFactory @Inject constructor(private val sessionHolder: Act
|
|||||||
userId = mergedEvent.root.senderId ?: "",
|
userId = mergedEvent.root.senderId ?: "",
|
||||||
avatarUrl = senderAvatar,
|
avatarUrl = senderAvatar,
|
||||||
memberName = senderName ?: "",
|
memberName = senderName ?: "",
|
||||||
eventId = mergedEvent.localId
|
localId = mergedEvent.localId,
|
||||||
|
eventId = mergedEvent.root.eventId ?: ""
|
||||||
)
|
)
|
||||||
mergedData.add(data)
|
mergedData.add(data)
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ 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
|
||||||
@ -58,7 +59,7 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : BaseEventItem<H>() {
|
|||||||
private val _readMarkerCallback = object : ReadMarkerView.Callback {
|
private val _readMarkerCallback = object : ReadMarkerView.Callback {
|
||||||
|
|
||||||
override fun onReadMarkerLongBound(isDisplayed: Boolean) {
|
override fun onReadMarkerLongBound(isDisplayed: Boolean) {
|
||||||
attributes.readReceiptsCallback?.onReadMarkerLongBound(isDisplayed)
|
attributes.readReceiptsCallback?.onReadMarkerLongBound(attributes.informationData.eventId, isDisplayed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,6 +158,10 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : BaseEventItem<H>() {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getEventId(): String? {
|
||||||
|
return 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) {
|
||||||
root.isClickable = attributes.informationData.sendState.isSent()
|
root.isClickable = attributes.informationData.sendState.isSent()
|
||||||
val state = if (attributes.informationData.hasPendingEdits) SendState.UNSENT else attributes.informationData.sendState
|
val state = if (attributes.informationData.hasPendingEdits) SendState.UNSENT else attributes.informationData.sendState
|
||||||
|
@ -44,10 +44,13 @@ 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?
|
||||||
|
|
||||||
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)
|
||||||
val checkableBackground by bind<CheckableView>(R.id.messageSelectedBackground)
|
val checkableBackground by bind<CheckableView>(R.id.messageSelectedBackground)
|
||||||
|
@ -55,6 +55,10 @@ abstract class DefaultItem : BaseEventItem<DefaultItem.Holder>() {
|
|||||||
holder.readReceiptsView.render(informationData.readReceipts, avatarRenderer, _readReceiptsClickListener)
|
holder.readReceiptsView.render(informationData.readReceipts, avatarRenderer, _readReceiptsClickListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getEventId(): String? {
|
||||||
|
return informationData.eventId
|
||||||
|
}
|
||||||
|
|
||||||
override fun getViewType() = STUB_ID
|
override fun getViewType() = STUB_ID
|
||||||
|
|
||||||
class Holder : BaseHolder(STUB_ID) {
|
class Holder : BaseHolder(STUB_ID) {
|
||||||
|
@ -42,7 +42,7 @@ abstract class MergedHeaderItem : BaseEventItem<MergedHeaderItem.Holder>() {
|
|||||||
private val _readMarkerCallback = object : ReadMarkerView.Callback {
|
private val _readMarkerCallback = object : ReadMarkerView.Callback {
|
||||||
|
|
||||||
override fun onReadMarkerLongBound(isDisplayed: Boolean) {
|
override fun onReadMarkerLongBound(isDisplayed: Boolean) {
|
||||||
attributes.readReceiptsCallback?.onReadMarkerLongBound(isDisplayed)
|
attributes.readReceiptsCallback?.onReadMarkerLongBound(attributes.readMarkerId ?: "", isDisplayed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,8 +89,14 @@ abstract class MergedHeaderItem : BaseEventItem<MergedHeaderItem.Holder>() {
|
|||||||
super.unbind(holder)
|
super.unbind(holder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun getEventId(): String? {
|
||||||
|
return attributes.mergeData.firstOrNull()?.eventId
|
||||||
|
}
|
||||||
|
|
||||||
data class Data(
|
data class Data(
|
||||||
val eventId: Long,
|
val localId: Long,
|
||||||
|
val eventId: String,
|
||||||
val userId: String,
|
val userId: String,
|
||||||
val memberName: String,
|
val memberName: String,
|
||||||
val avatarUrl: String?
|
val avatarUrl: String?
|
||||||
|
@ -40,7 +40,7 @@ abstract class NoticeItem : BaseEventItem<NoticeItem.Holder>() {
|
|||||||
private val _readMarkerCallback = object : ReadMarkerView.Callback {
|
private val _readMarkerCallback = object : ReadMarkerView.Callback {
|
||||||
|
|
||||||
override fun onReadMarkerLongBound(isDisplayed: Boolean) {
|
override fun onReadMarkerLongBound(isDisplayed: Boolean) {
|
||||||
attributes.readReceiptsCallback?.onReadMarkerLongBound(isDisplayed)
|
attributes.readReceiptsCallback?.onReadMarkerLongBound(attributes.informationData.eventId, isDisplayed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,6 +69,11 @@ abstract class NoticeItem : BaseEventItem<NoticeItem.Holder>() {
|
|||||||
super.unbind(holder)
|
super.unbind(holder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun getEventId(): String? {
|
||||||
|
return attributes.informationData.eventId
|
||||||
|
}
|
||||||
|
|
||||||
override fun getViewType() = STUB_ID
|
override fun getViewType() = STUB_ID
|
||||||
|
|
||||||
class Holder : BaseHolder(STUB_ID) {
|
class Holder : BaseHolder(STUB_ID) {
|
||||||
|
@ -124,10 +124,10 @@
|
|||||||
android:id="@+id/jumpToReadMarkerView"
|
android:id="@+id/jumpToReadMarkerView"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:visibility="gone"
|
android:visibility="invisible"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/syncProgressBarWrap" />
|
app:layout_constraintTop_toBottomOf="@id/syncStateView" />
|
||||||
|
|
||||||
<im.vector.riotx.core.ui.views.NotificationAreaView
|
<im.vector.riotx.core.ui.views.NotificationAreaView
|
||||||
android:id="@+id/notificationAreaView"
|
android:id="@+id/notificationAreaView"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user