forked from GitHub-Mirror/riotX-android
Timeline : fix some timeline rendering issues (senderName, merge item, left event). Still need to work on it.
This commit is contained in:
@ -20,7 +20,7 @@ import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.InstrumentedTest
|
||||
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.internal.session.room.members.RoomMemberExtractor
|
||||
import im.vector.matrix.android.internal.session.room.members.SenderRoomMemberExtractor
|
||||
import im.vector.matrix.android.internal.session.room.timeline.DefaultTimeline
|
||||
import im.vector.matrix.android.internal.session.room.timeline.TimelineEventFactory
|
||||
import im.vector.matrix.android.internal.session.room.timeline.TokenChunkEventPersistor
|
||||
@ -57,7 +57,7 @@ internal class TimelineTest : InstrumentedTest {
|
||||
val tokenChunkEventPersistor = TokenChunkEventPersistor(monarchy)
|
||||
val paginationTask = FakePaginationTask(tokenChunkEventPersistor)
|
||||
val getContextOfEventTask = FakeGetContextOfEventTask(tokenChunkEventPersistor)
|
||||
val roomMemberExtractor = RoomMemberExtractor(ROOM_ID)
|
||||
val roomMemberExtractor = SenderRoomMemberExtractor(ROOM_ID)
|
||||
val timelineEventFactory = TimelineEventFactory(roomMemberExtractor)
|
||||
return DefaultTimeline(ROOM_ID, initialEventId, monarchy.realmConfiguration, taskExecutor, getContextOfEventTask, timelineEventFactory, paginationTask, null)
|
||||
}
|
||||
|
@ -29,7 +29,8 @@ data class TimelineEvent(
|
||||
val root: Event,
|
||||
val localId: String,
|
||||
val displayIndex: Int,
|
||||
val roomMember: RoomMember?,
|
||||
val senderName: String?,
|
||||
val senderAvatar: String?,
|
||||
val sendState: SendState
|
||||
) {
|
||||
|
||||
|
@ -56,14 +56,10 @@ internal fun ChunkEntity.merge(roomId: String,
|
||||
if (direction == PaginationDirection.FORWARDS) {
|
||||
this.nextToken = chunkToMerge.nextToken
|
||||
this.isLastForward = chunkToMerge.isLastForward
|
||||
this.forwardsStateIndex = chunkToMerge.forwardsStateIndex
|
||||
this.forwardsDisplayIndex = chunkToMerge.forwardsDisplayIndex
|
||||
eventsToMerge = chunkToMerge.events.sort(EventEntityFields.DISPLAY_INDEX, Sort.ASCENDING)
|
||||
} else {
|
||||
this.prevToken = chunkToMerge.prevToken
|
||||
this.isLastBackward = chunkToMerge.isLastBackward
|
||||
this.backwardsStateIndex = chunkToMerge.backwardsStateIndex
|
||||
this.backwardsDisplayIndex = chunkToMerge.backwardsDisplayIndex
|
||||
eventsToMerge = chunkToMerge.events.sort(EventEntityFields.DISPLAY_INDEX, Sort.DESCENDING)
|
||||
}
|
||||
eventsToMerge.forEach {
|
||||
@ -119,20 +115,20 @@ internal fun ChunkEntity.add(roomId: String,
|
||||
this.displayIndex = currentDisplayIndex
|
||||
this.sendState = SendState.SYNCED
|
||||
}
|
||||
// We are not using the order of the list, but will be sorting with displayIndex field
|
||||
events.add(eventEntity)
|
||||
val position = if (direction == PaginationDirection.FORWARDS) 0 else this.events.size
|
||||
events.add(position, eventEntity)
|
||||
}
|
||||
|
||||
internal fun ChunkEntity.lastDisplayIndex(direction: PaginationDirection, defaultValue: Int = 0): Int {
|
||||
return when (direction) {
|
||||
PaginationDirection.FORWARDS -> forwardsDisplayIndex
|
||||
PaginationDirection.BACKWARDS -> backwardsDisplayIndex
|
||||
} ?: defaultValue
|
||||
PaginationDirection.FORWARDS -> forwardsDisplayIndex
|
||||
PaginationDirection.BACKWARDS -> backwardsDisplayIndex
|
||||
} ?: defaultValue
|
||||
}
|
||||
|
||||
internal fun ChunkEntity.lastStateIndex(direction: PaginationDirection, defaultValue: Int = 0): Int {
|
||||
return when (direction) {
|
||||
PaginationDirection.FORWARDS -> forwardsStateIndex
|
||||
PaginationDirection.BACKWARDS -> backwardsStateIndex
|
||||
} ?: defaultValue
|
||||
PaginationDirection.FORWARDS -> forwardsStateIndex
|
||||
PaginationDirection.BACKWARDS -> backwardsStateIndex
|
||||
} ?: defaultValue
|
||||
}
|
@ -21,7 +21,7 @@ import im.vector.matrix.android.api.session.room.Room
|
||||
import im.vector.matrix.android.internal.session.room.invite.InviteTask
|
||||
import im.vector.matrix.android.internal.session.room.members.DefaultRoomMembersService
|
||||
import im.vector.matrix.android.internal.session.room.members.LoadRoomMembersTask
|
||||
import im.vector.matrix.android.internal.session.room.members.RoomMemberExtractor
|
||||
import im.vector.matrix.android.internal.session.room.members.SenderRoomMemberExtractor
|
||||
import im.vector.matrix.android.internal.session.room.read.DefaultReadService
|
||||
import im.vector.matrix.android.internal.session.room.read.SetReadMarkersTask
|
||||
import im.vector.matrix.android.internal.session.room.send.DefaultSendService
|
||||
@ -45,7 +45,7 @@ internal class RoomFactory(private val loadRoomMembersTask: LoadRoomMembersTask,
|
||||
private val taskExecutor: TaskExecutor) {
|
||||
|
||||
fun instantiate(roomId: String): Room {
|
||||
val roomMemberExtractor = RoomMemberExtractor(roomId)
|
||||
val roomMemberExtractor = SenderRoomMemberExtractor(roomId)
|
||||
val timelineEventFactory = TimelineEventFactory(roomMemberExtractor)
|
||||
val timelineService = DefaultTimelineService(roomId, monarchy, taskExecutor, contextOfEventTask, timelineEventFactory, paginationTask)
|
||||
val sendService = DefaultSendService(roomId, eventFactory, monarchy)
|
||||
|
@ -48,6 +48,18 @@ internal class RoomMembers(private val realm: Realm,
|
||||
}
|
||||
}
|
||||
|
||||
fun isUniqueDisplayName(displayName: String?): Boolean {
|
||||
if(displayName.isNullOrEmpty()){
|
||||
return true
|
||||
}
|
||||
return EventEntity
|
||||
.where(realm, roomId, EventType.STATE_ROOM_MEMBER)
|
||||
.contains(EventEntityFields.CONTENT, displayName)
|
||||
.distinct(EventEntityFields.STATE_KEY)
|
||||
.findAll()
|
||||
.size == 1
|
||||
}
|
||||
|
||||
fun queryRoomMembersEvent(): RealmQuery<EventEntity> {
|
||||
return EventEntity
|
||||
.where(realm, roomId, EventType.STATE_ROOM_MEMBER)
|
||||
|
@ -20,48 +20,45 @@ import im.vector.matrix.android.api.session.events.model.EventType
|
||||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.model.RoomMember
|
||||
import im.vector.matrix.android.internal.database.mapper.ContentMapper
|
||||
import im.vector.matrix.android.internal.database.model.ChunkEntity
|
||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||
import im.vector.matrix.android.internal.database.model.EventEntityFields
|
||||
import im.vector.matrix.android.internal.database.model.RoomEntity
|
||||
import im.vector.matrix.android.internal.database.query.findIncludingEvent
|
||||
import im.vector.matrix.android.internal.database.query.next
|
||||
import im.vector.matrix.android.internal.database.query.prev
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmList
|
||||
import io.realm.RealmQuery
|
||||
|
||||
internal class RoomMemberExtractor(private val roomId: String) {
|
||||
|
||||
private val cached = HashMap<String, RoomMember?>()
|
||||
internal class SenderRoomMemberExtractor(private val roomId: String) {
|
||||
|
||||
fun extractFrom(event: EventEntity): RoomMember? {
|
||||
val sender = event.sender ?: return null
|
||||
val cacheKey = sender + event.stateIndex
|
||||
if (cached.containsKey(cacheKey)) {
|
||||
return cached[cacheKey]
|
||||
}
|
||||
// If the event is unlinked we want to fetch unlinked state events
|
||||
val unlinked = event.isUnlinked
|
||||
// When stateIndex is negative, we try to get the next stateEvent prevContent()
|
||||
// If prevContent is null we fallback to the Int.MIN state events content()
|
||||
val content = if (event.stateIndex <= 0) {
|
||||
baseQuery(event.realm, roomId, sender, unlinked).next(from = event.stateIndex)?.prevContent
|
||||
?: baseQuery(event.realm, roomId, sender, unlinked).prev(since = event.stateIndex)?.content
|
||||
} else {
|
||||
baseQuery(event.realm, roomId, sender, unlinked).prev(since = event.stateIndex)?.content
|
||||
val roomEntity = RoomEntity.where(event.realm, roomId = roomId).findFirst() ?: return null
|
||||
val chunkEntity = ChunkEntity.findIncludingEvent(event.realm, event.eventId)
|
||||
val content = when {
|
||||
chunkEntity == null -> null
|
||||
event.stateIndex <= 0 -> baseQuery(chunkEntity.events, sender, unlinked).next(from = event.stateIndex)?.prevContent
|
||||
else -> baseQuery(chunkEntity.events, sender, unlinked).prev(since = event.stateIndex)?.content
|
||||
}
|
||||
val roomMember: RoomMember? = ContentMapper.map(content).toModel()
|
||||
cached[cacheKey] = roomMember
|
||||
return roomMember
|
||||
|
||||
val fallbackContent = content
|
||||
?: baseQuery(roomEntity.untimelinedStateEvents, sender, unlinked).prev(since = event.stateIndex)?.content
|
||||
|
||||
return ContentMapper.map(fallbackContent).toModel()
|
||||
}
|
||||
|
||||
private fun baseQuery(realm: Realm,
|
||||
roomId: String,
|
||||
private fun baseQuery(list: RealmList<EventEntity>,
|
||||
sender: String,
|
||||
isUnlinked: Boolean): RealmQuery<EventEntity> {
|
||||
|
||||
val filterMode = if (isUnlinked) EventEntity.LinkFilterMode.UNLINKED_ONLY else EventEntity.LinkFilterMode.LINKED_ONLY
|
||||
return EventEntity
|
||||
.where(realm, roomId = roomId, type = EventType.STATE_ROOM_MEMBER, linkFilterMode = filterMode)
|
||||
return list
|
||||
.where()
|
||||
.equalTo(EventEntityFields.STATE_KEY, sender)
|
||||
.equalTo(EventEntityFields.TYPE, EventType.STATE_ROOM_MEMBER)
|
||||
.equalTo(EventEntityFields.IS_UNLINKED, isUnlinked)
|
||||
}
|
||||
|
||||
}
|
@ -27,14 +27,24 @@ 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.util.CancelableBag
|
||||
import im.vector.matrix.android.api.util.addTo
|
||||
import im.vector.matrix.android.internal.database.model.*
|
||||
import im.vector.matrix.android.internal.database.model.ChunkEntity
|
||||
import im.vector.matrix.android.internal.database.model.ChunkEntityFields
|
||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||
import im.vector.matrix.android.internal.database.model.EventEntityFields
|
||||
import im.vector.matrix.android.internal.database.model.RoomEntity
|
||||
import im.vector.matrix.android.internal.database.query.findIncludingEvent
|
||||
import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
import im.vector.matrix.android.internal.task.configureWith
|
||||
import im.vector.matrix.android.internal.util.Debouncer
|
||||
import io.realm.*
|
||||
import io.realm.OrderedCollectionChangeSet
|
||||
import io.realm.OrderedRealmCollectionChangeListener
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmConfiguration
|
||||
import io.realm.RealmQuery
|
||||
import io.realm.RealmResults
|
||||
import io.realm.Sort
|
||||
import timber.log.Timber
|
||||
import java.util.*
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
@ -87,9 +97,16 @@ internal class DefaultTimeline(
|
||||
|
||||
|
||||
private val eventsChangeListener = OrderedRealmCollectionChangeListener<RealmResults<EventEntity>> { _, changeSet ->
|
||||
if (changeSet.state == OrderedCollectionChangeSet.State.INITIAL) {
|
||||
if (changeSet.state == OrderedCollectionChangeSet.State.INITIAL ) {
|
||||
handleInitialLoad()
|
||||
} else {
|
||||
// If changeSet has deletion we are having a gap, so we clear everything
|
||||
if(changeSet.deletionRanges.isNotEmpty()){
|
||||
prevDisplayIndex = DISPLAY_INDEX_UNKNOWN
|
||||
nextDisplayIndex = DISPLAY_INDEX_UNKNOWN
|
||||
builtEvents.clear()
|
||||
timelineEventFactory.clear()
|
||||
}
|
||||
changeSet.insertionRanges.forEach { range ->
|
||||
val (startDisplayIndex, direction) = if (range.startIndex == 0) {
|
||||
Pair(liveEvents[range.length - 1]!!.displayIndex, Timeline.Direction.FORWARDS)
|
||||
@ -108,6 +125,7 @@ internal class DefaultTimeline(
|
||||
buildTimelineEvents(startDisplayIndex, direction, range.length.toLong())
|
||||
postSnapshot()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -298,9 +316,9 @@ internal class DefaultTimeline(
|
||||
private fun executePaginationTask(direction: Timeline.Direction, limit: Int) {
|
||||
val token = getTokenLive(direction) ?: return
|
||||
val params = PaginationTask.Params(roomId = roomId,
|
||||
from = token,
|
||||
direction = direction.toPaginationDirection(),
|
||||
limit = limit)
|
||||
from = token,
|
||||
direction = direction.toPaginationDirection(),
|
||||
limit = limit)
|
||||
|
||||
Timber.v("Should fetch $limit items $direction")
|
||||
paginationTask.configureWith(params)
|
||||
|
@ -19,19 +19,36 @@ package im.vector.matrix.android.internal.session.room.timeline
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||
import im.vector.matrix.android.internal.database.mapper.asDomain
|
||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||
import im.vector.matrix.android.internal.session.room.members.RoomMemberExtractor
|
||||
import im.vector.matrix.android.internal.session.room.members.SenderRoomMemberExtractor
|
||||
|
||||
internal class TimelineEventFactory(private val roomMemberExtractor: RoomMemberExtractor) {
|
||||
internal class TimelineEventFactory(private val roomMemberExtractor: SenderRoomMemberExtractor) {
|
||||
|
||||
private val cached = mutableMapOf<String, SenderData>()
|
||||
|
||||
fun create(eventEntity: EventEntity): TimelineEvent {
|
||||
val roomMember = roomMemberExtractor.extractFrom(eventEntity)
|
||||
val sender = eventEntity.sender
|
||||
val cacheKey = sender + eventEntity.stateIndex
|
||||
val senderData = cached.getOrPut(cacheKey) {
|
||||
val senderRoomMember = roomMemberExtractor.extractFrom(eventEntity)
|
||||
SenderData(senderRoomMember?.displayName, senderRoomMember?.avatarUrl)
|
||||
}
|
||||
return TimelineEvent(
|
||||
eventEntity.asDomain(),
|
||||
eventEntity.localId,
|
||||
eventEntity.displayIndex,
|
||||
roomMember,
|
||||
senderData.senderName,
|
||||
senderData.senderAvatar,
|
||||
eventEntity.sendState
|
||||
)
|
||||
}
|
||||
|
||||
fun clear(){
|
||||
cached.clear()
|
||||
}
|
||||
|
||||
private data class SenderData(
|
||||
val senderName: String?,
|
||||
val senderAvatar: String?
|
||||
)
|
||||
|
||||
}
|
Reference in New Issue
Block a user