mirror of
https://github.com/element-hq/element-android
synced 2024-11-23 09:55:40 +03:00
Timeline : fix some timeline rendering issues (senderName, merge item, left event). Still need to work on it.
This commit is contained in:
parent
287feace12
commit
694df9d845
17 changed files with 210 additions and 128 deletions
|
@ -20,7 +20,7 @@ import com.zhuinden.monarchy.Monarchy
|
||||||
import im.vector.matrix.android.InstrumentedTest
|
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.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.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.DefaultTimeline
|
||||||
import im.vector.matrix.android.internal.session.room.timeline.TimelineEventFactory
|
import im.vector.matrix.android.internal.session.room.timeline.TimelineEventFactory
|
||||||
import im.vector.matrix.android.internal.session.room.timeline.TokenChunkEventPersistor
|
import im.vector.matrix.android.internal.session.room.timeline.TokenChunkEventPersistor
|
||||||
|
@ -57,7 +57,7 @@ internal class TimelineTest : InstrumentedTest {
|
||||||
val tokenChunkEventPersistor = TokenChunkEventPersistor(monarchy)
|
val tokenChunkEventPersistor = TokenChunkEventPersistor(monarchy)
|
||||||
val paginationTask = FakePaginationTask(tokenChunkEventPersistor)
|
val paginationTask = FakePaginationTask(tokenChunkEventPersistor)
|
||||||
val getContextOfEventTask = FakeGetContextOfEventTask(tokenChunkEventPersistor)
|
val getContextOfEventTask = FakeGetContextOfEventTask(tokenChunkEventPersistor)
|
||||||
val roomMemberExtractor = RoomMemberExtractor(ROOM_ID)
|
val roomMemberExtractor = SenderRoomMemberExtractor(ROOM_ID)
|
||||||
val timelineEventFactory = TimelineEventFactory(roomMemberExtractor)
|
val timelineEventFactory = TimelineEventFactory(roomMemberExtractor)
|
||||||
return DefaultTimeline(ROOM_ID, initialEventId, monarchy.realmConfiguration, taskExecutor, getContextOfEventTask, timelineEventFactory, paginationTask, null)
|
return DefaultTimeline(ROOM_ID, initialEventId, monarchy.realmConfiguration, taskExecutor, getContextOfEventTask, timelineEventFactory, paginationTask, null)
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,8 @@ data class TimelineEvent(
|
||||||
val root: Event,
|
val root: Event,
|
||||||
val localId: String,
|
val localId: String,
|
||||||
val displayIndex: Int,
|
val displayIndex: Int,
|
||||||
val roomMember: RoomMember?,
|
val senderName: String?,
|
||||||
|
val senderAvatar: String?,
|
||||||
val sendState: SendState
|
val sendState: SendState
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
|
@ -56,14 +56,10 @@ internal fun ChunkEntity.merge(roomId: String,
|
||||||
if (direction == PaginationDirection.FORWARDS) {
|
if (direction == PaginationDirection.FORWARDS) {
|
||||||
this.nextToken = chunkToMerge.nextToken
|
this.nextToken = chunkToMerge.nextToken
|
||||||
this.isLastForward = chunkToMerge.isLastForward
|
this.isLastForward = chunkToMerge.isLastForward
|
||||||
this.forwardsStateIndex = chunkToMerge.forwardsStateIndex
|
|
||||||
this.forwardsDisplayIndex = chunkToMerge.forwardsDisplayIndex
|
|
||||||
eventsToMerge = chunkToMerge.events.sort(EventEntityFields.DISPLAY_INDEX, Sort.ASCENDING)
|
eventsToMerge = chunkToMerge.events.sort(EventEntityFields.DISPLAY_INDEX, Sort.ASCENDING)
|
||||||
} else {
|
} else {
|
||||||
this.prevToken = chunkToMerge.prevToken
|
this.prevToken = chunkToMerge.prevToken
|
||||||
this.isLastBackward = chunkToMerge.isLastBackward
|
this.isLastBackward = chunkToMerge.isLastBackward
|
||||||
this.backwardsStateIndex = chunkToMerge.backwardsStateIndex
|
|
||||||
this.backwardsDisplayIndex = chunkToMerge.backwardsDisplayIndex
|
|
||||||
eventsToMerge = chunkToMerge.events.sort(EventEntityFields.DISPLAY_INDEX, Sort.DESCENDING)
|
eventsToMerge = chunkToMerge.events.sort(EventEntityFields.DISPLAY_INDEX, Sort.DESCENDING)
|
||||||
}
|
}
|
||||||
eventsToMerge.forEach {
|
eventsToMerge.forEach {
|
||||||
|
@ -119,8 +115,8 @@ internal fun ChunkEntity.add(roomId: String,
|
||||||
this.displayIndex = currentDisplayIndex
|
this.displayIndex = currentDisplayIndex
|
||||||
this.sendState = SendState.SYNCED
|
this.sendState = SendState.SYNCED
|
||||||
}
|
}
|
||||||
// We are not using the order of the list, but will be sorting with displayIndex field
|
val position = if (direction == PaginationDirection.FORWARDS) 0 else this.events.size
|
||||||
events.add(eventEntity)
|
events.add(position, eventEntity)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun ChunkEntity.lastDisplayIndex(direction: PaginationDirection, defaultValue: Int = 0): Int {
|
internal fun ChunkEntity.lastDisplayIndex(direction: PaginationDirection, defaultValue: Int = 0): Int {
|
||||||
|
|
|
@ -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.invite.InviteTask
|
||||||
import im.vector.matrix.android.internal.session.room.members.DefaultRoomMembersService
|
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.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.DefaultReadService
|
||||||
import im.vector.matrix.android.internal.session.room.read.SetReadMarkersTask
|
import im.vector.matrix.android.internal.session.room.read.SetReadMarkersTask
|
||||||
import im.vector.matrix.android.internal.session.room.send.DefaultSendService
|
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) {
|
private val taskExecutor: TaskExecutor) {
|
||||||
|
|
||||||
fun instantiate(roomId: String): Room {
|
fun instantiate(roomId: String): Room {
|
||||||
val roomMemberExtractor = RoomMemberExtractor(roomId)
|
val roomMemberExtractor = SenderRoomMemberExtractor(roomId)
|
||||||
val timelineEventFactory = TimelineEventFactory(roomMemberExtractor)
|
val timelineEventFactory = TimelineEventFactory(roomMemberExtractor)
|
||||||
val timelineService = DefaultTimelineService(roomId, monarchy, taskExecutor, contextOfEventTask, timelineEventFactory, paginationTask)
|
val timelineService = DefaultTimelineService(roomId, monarchy, taskExecutor, contextOfEventTask, timelineEventFactory, paginationTask)
|
||||||
val sendService = DefaultSendService(roomId, eventFactory, monarchy)
|
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> {
|
fun queryRoomMembersEvent(): RealmQuery<EventEntity> {
|
||||||
return EventEntity
|
return EventEntity
|
||||||
.where(realm, roomId, EventType.STATE_ROOM_MEMBER)
|
.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.events.model.toModel
|
||||||
import im.vector.matrix.android.api.session.room.model.RoomMember
|
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.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.EventEntity
|
||||||
import im.vector.matrix.android.internal.database.model.EventEntityFields
|
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.next
|
||||||
import im.vector.matrix.android.internal.database.query.prev
|
import im.vector.matrix.android.internal.database.query.prev
|
||||||
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.RealmList
|
||||||
import io.realm.RealmQuery
|
import io.realm.RealmQuery
|
||||||
|
|
||||||
internal class RoomMemberExtractor(private val roomId: String) {
|
internal class SenderRoomMemberExtractor(private val roomId: String) {
|
||||||
|
|
||||||
private val cached = HashMap<String, RoomMember?>()
|
|
||||||
|
|
||||||
fun extractFrom(event: EventEntity): RoomMember? {
|
fun extractFrom(event: EventEntity): RoomMember? {
|
||||||
val sender = event.sender ?: return null
|
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
|
// If the event is unlinked we want to fetch unlinked state events
|
||||||
val unlinked = event.isUnlinked
|
val unlinked = event.isUnlinked
|
||||||
// When stateIndex is negative, we try to get the next stateEvent prevContent()
|
val roomEntity = RoomEntity.where(event.realm, roomId = roomId).findFirst() ?: return null
|
||||||
// If prevContent is null we fallback to the Int.MIN state events content()
|
val chunkEntity = ChunkEntity.findIncludingEvent(event.realm, event.eventId)
|
||||||
val content = if (event.stateIndex <= 0) {
|
val content = when {
|
||||||
baseQuery(event.realm, roomId, sender, unlinked).next(from = event.stateIndex)?.prevContent
|
chunkEntity == null -> null
|
||||||
?: baseQuery(event.realm, roomId, sender, unlinked).prev(since = event.stateIndex)?.content
|
event.stateIndex <= 0 -> baseQuery(chunkEntity.events, sender, unlinked).next(from = event.stateIndex)?.prevContent
|
||||||
} else {
|
else -> baseQuery(chunkEntity.events, sender, unlinked).prev(since = event.stateIndex)?.content
|
||||||
baseQuery(event.realm, roomId, sender, unlinked).prev(since = event.stateIndex)?.content
|
|
||||||
}
|
|
||||||
val roomMember: RoomMember? = ContentMapper.map(content).toModel()
|
|
||||||
cached[cacheKey] = roomMember
|
|
||||||
return roomMember
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun baseQuery(realm: Realm,
|
val fallbackContent = content
|
||||||
roomId: String,
|
?: baseQuery(roomEntity.untimelinedStateEvents, sender, unlinked).prev(since = event.stateIndex)?.content
|
||||||
|
|
||||||
|
return ContentMapper.map(fallbackContent).toModel()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun baseQuery(list: RealmList<EventEntity>,
|
||||||
sender: String,
|
sender: String,
|
||||||
isUnlinked: Boolean): RealmQuery<EventEntity> {
|
isUnlinked: Boolean): RealmQuery<EventEntity> {
|
||||||
|
return list
|
||||||
val filterMode = if (isUnlinked) EventEntity.LinkFilterMode.UNLINKED_ONLY else EventEntity.LinkFilterMode.LINKED_ONLY
|
.where()
|
||||||
return EventEntity
|
|
||||||
.where(realm, roomId = roomId, type = EventType.STATE_ROOM_MEMBER, linkFilterMode = filterMode)
|
|
||||||
.equalTo(EventEntityFields.STATE_KEY, sender)
|
.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.session.room.timeline.TimelineEvent
|
||||||
import im.vector.matrix.android.api.util.CancelableBag
|
import im.vector.matrix.android.api.util.CancelableBag
|
||||||
import im.vector.matrix.android.api.util.addTo
|
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.findIncludingEvent
|
||||||
import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom
|
import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom
|
||||||
import im.vector.matrix.android.internal.database.query.where
|
import im.vector.matrix.android.internal.database.query.where
|
||||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||||
import im.vector.matrix.android.internal.task.configureWith
|
import im.vector.matrix.android.internal.task.configureWith
|
||||||
import im.vector.matrix.android.internal.util.Debouncer
|
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 timber.log.Timber
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
@ -87,9 +97,16 @@ internal class DefaultTimeline(
|
||||||
|
|
||||||
|
|
||||||
private val eventsChangeListener = OrderedRealmCollectionChangeListener<RealmResults<EventEntity>> { _, changeSet ->
|
private val eventsChangeListener = OrderedRealmCollectionChangeListener<RealmResults<EventEntity>> { _, changeSet ->
|
||||||
if (changeSet.state == OrderedCollectionChangeSet.State.INITIAL) {
|
if (changeSet.state == OrderedCollectionChangeSet.State.INITIAL ) {
|
||||||
handleInitialLoad()
|
handleInitialLoad()
|
||||||
} else {
|
} 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 ->
|
changeSet.insertionRanges.forEach { range ->
|
||||||
val (startDisplayIndex, direction) = if (range.startIndex == 0) {
|
val (startDisplayIndex, direction) = if (range.startIndex == 0) {
|
||||||
Pair(liveEvents[range.length - 1]!!.displayIndex, Timeline.Direction.FORWARDS)
|
Pair(liveEvents[range.length - 1]!!.displayIndex, Timeline.Direction.FORWARDS)
|
||||||
|
@ -108,6 +125,7 @@ internal class DefaultTimeline(
|
||||||
buildTimelineEvents(startDisplayIndex, direction, range.length.toLong())
|
buildTimelineEvents(startDisplayIndex, direction, range.length.toLong())
|
||||||
postSnapshot()
|
postSnapshot()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.api.session.room.timeline.TimelineEvent
|
||||||
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.EventEntity
|
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 {
|
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(
|
return TimelineEvent(
|
||||||
eventEntity.asDomain(),
|
eventEntity.asDomain(),
|
||||||
eventEntity.localId,
|
eventEntity.localId,
|
||||||
eventEntity.displayIndex,
|
eventEntity.displayIndex,
|
||||||
roomMember,
|
senderData.senderName,
|
||||||
|
senderData.senderAvatar,
|
||||||
eventEntity.sendState
|
eventEntity.sendState
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun clear(){
|
||||||
|
cached.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class SenderData(
|
||||||
|
val senderName: String?,
|
||||||
|
val senderAvatar: String?
|
||||||
|
)
|
||||||
|
|
||||||
}
|
}
|
|
@ -24,6 +24,8 @@ import androidx.recyclerview.widget.ListUpdateCallback
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
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 im.vector.matrix.android.api.session.events.model.toModel
|
||||||
|
import im.vector.matrix.android.api.session.room.model.RoomMember
|
||||||
import im.vector.matrix.android.api.session.room.model.message.MessageAudioContent
|
import im.vector.matrix.android.api.session.room.model.message.MessageAudioContent
|
||||||
import im.vector.matrix.android.api.session.room.model.message.MessageFileContent
|
import im.vector.matrix.android.api.session.room.model.message.MessageFileContent
|
||||||
import im.vector.matrix.android.api.session.room.model.message.MessageImageContent
|
import im.vector.matrix.android.api.session.room.model.message.MessageImageContent
|
||||||
|
@ -33,7 +35,15 @@ import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
import im.vector.riotredesign.core.epoxy.LoadingItemModel_
|
import im.vector.riotredesign.core.epoxy.LoadingItemModel_
|
||||||
import im.vector.riotredesign.core.extensions.localDateTime
|
import im.vector.riotredesign.core.extensions.localDateTime
|
||||||
import im.vector.riotredesign.features.home.room.detail.timeline.factory.TimelineItemFactory
|
import im.vector.riotredesign.features.home.room.detail.timeline.factory.TimelineItemFactory
|
||||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.*
|
import im.vector.riotredesign.features.home.room.detail.timeline.helper.RoomMemberEventHelper
|
||||||
|
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineAsyncHelper
|
||||||
|
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter
|
||||||
|
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineEventDiffUtilCallback
|
||||||
|
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineEventVisibilityStateChangedListener
|
||||||
|
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
|
||||||
|
import im.vector.riotredesign.features.home.room.detail.timeline.helper.canBeMerged
|
||||||
|
import im.vector.riotredesign.features.home.room.detail.timeline.helper.nextDisplayableEvent
|
||||||
|
import im.vector.riotredesign.features.home.room.detail.timeline.helper.prevSameTypeEvents
|
||||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.DaySeparatorItem
|
import im.vector.riotredesign.features.home.room.detail.timeline.item.DaySeparatorItem
|
||||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.DaySeparatorItem_
|
import im.vector.riotredesign.features.home.room.detail.timeline.item.DaySeparatorItem_
|
||||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.MergedHeaderItem
|
import im.vector.riotredesign.features.home.room.detail.timeline.item.MergedHeaderItem
|
||||||
|
@ -217,24 +227,32 @@ class TimelineEventController(private val dateFormatter: TimelineDateFormatter,
|
||||||
if (prevSameTypeEvents.isEmpty()) {
|
if (prevSameTypeEvents.isEmpty()) {
|
||||||
null
|
null
|
||||||
} else {
|
} else {
|
||||||
val mergedEvents = (listOf(event) + prevSameTypeEvents)
|
val mergedEvents = (prevSameTypeEvents + listOf(event)).asReversed()
|
||||||
val mergedData = mergedEvents.map {
|
val mergedData = mergedEvents.map { mergedEvent ->
|
||||||
val roomMember = event.roomMember
|
val eventContent: RoomMember? = mergedEvent.root.content.toModel()
|
||||||
|
val prevEventContent: RoomMember? = mergedEvent.root.prevContent.toModel()
|
||||||
|
val senderAvatar = RoomMemberEventHelper.senderAvatar(eventContent, prevEventContent, mergedEvent)
|
||||||
|
val senderName = RoomMemberEventHelper.senderName(eventContent, prevEventContent, mergedEvent)
|
||||||
MergedHeaderItem.Data(
|
MergedHeaderItem.Data(
|
||||||
userId = event.root.sender ?: "",
|
userId = mergedEvent.root.sender ?: "",
|
||||||
avatarUrl = roomMember?.avatarUrl,
|
avatarUrl = senderAvatar,
|
||||||
memberName = roomMember?.displayName ?: "",
|
memberName = senderName ?: "",
|
||||||
eventId = it.localId
|
eventId = mergedEvent.localId
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val mergedEventIds = mergedEvents.map { it.localId }
|
val mergedEventIds = mergedEvents.map { it.localId }
|
||||||
val mergeId = mergedEventIds.joinToString(separator = "_") { it }
|
// We try to find if one of the item id were used as mergeItemCollapseStates key
|
||||||
val isCollapsed = mergeItemCollapseStates.getOrPut(event.localId) { true }
|
// => handle case where paginating from mergeable events and we get more
|
||||||
|
val previousCollapseStateKey = mergedEventIds.intersect(mergeItemCollapseStates.keys).firstOrNull()
|
||||||
|
val initialCollapseState = mergeItemCollapseStates.remove(previousCollapseStateKey)
|
||||||
|
?: true
|
||||||
|
val isCollapsed = mergeItemCollapseStates.getOrPut(event.localId) { initialCollapseState }
|
||||||
if (isCollapsed) {
|
if (isCollapsed) {
|
||||||
collapsedEventIds.addAll(mergedEventIds)
|
collapsedEventIds.addAll(mergedEventIds)
|
||||||
} else {
|
} else {
|
||||||
collapsedEventIds.removeAll(mergedEventIds)
|
collapsedEventIds.removeAll(mergedEventIds)
|
||||||
}
|
}
|
||||||
|
val mergeId = mergedEventIds.joinToString(separator = "_") { it }
|
||||||
MergedHeaderItem(isCollapsed, mergeId, mergedData) {
|
MergedHeaderItem(isCollapsed, mergeId, mergedData) {
|
||||||
mergeItemCollapseStates[event.localId] = it
|
mergeItemCollapseStates[event.localId] = it
|
||||||
requestModelBuild()
|
requestModelBuild()
|
||||||
|
|
|
@ -30,27 +30,26 @@ import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem
|
||||||
class CallItemFactory(private val stringProvider: StringProvider) {
|
class CallItemFactory(private val stringProvider: StringProvider) {
|
||||||
|
|
||||||
fun create(event: TimelineEvent): NoticeItem? {
|
fun create(event: TimelineEvent): NoticeItem? {
|
||||||
val roomMember = event.roomMember ?: return null
|
val text = buildNoticeText(event.root, event.senderName) ?: return null
|
||||||
val text = buildNoticeText(event.root, roomMember) ?: return null
|
|
||||||
return NoticeItem_()
|
return NoticeItem_()
|
||||||
.noticeText(text)
|
.noticeText(text)
|
||||||
.avatarUrl(roomMember.avatarUrl)
|
.avatarUrl(event.senderAvatar)
|
||||||
.memberName(roomMember.displayName)
|
.memberName(event.senderName)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildNoticeText(event: Event, roomMember: RoomMember): CharSequence? {
|
private fun buildNoticeText(event: Event, senderName: String?): CharSequence? {
|
||||||
return when {
|
return when {
|
||||||
EventType.CALL_INVITE == event.type -> {
|
EventType.CALL_INVITE == event.type -> {
|
||||||
val content = event.content.toModel<CallInviteContent>() ?: return null
|
val content = event.content.toModel<CallInviteContent>() ?: return null
|
||||||
val isVideoCall = content.offer.sdp == CallInviteContent.Offer.SDP_VIDEO
|
val isVideoCall = content.offer.sdp == CallInviteContent.Offer.SDP_VIDEO
|
||||||
return if (isVideoCall) {
|
return if (isVideoCall) {
|
||||||
stringProvider.getString(R.string.notice_placed_video_call, roomMember.displayName)
|
stringProvider.getString(R.string.notice_placed_video_call, senderName)
|
||||||
} else {
|
} else {
|
||||||
stringProvider.getString(R.string.notice_placed_voice_call, roomMember.displayName)
|
stringProvider.getString(R.string.notice_placed_voice_call, senderName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EventType.CALL_ANSWER == event.type -> stringProvider.getString(R.string.notice_answered_call, roomMember.displayName)
|
EventType.CALL_ANSWER == event.type -> stringProvider.getString(R.string.notice_answered_call, senderName)
|
||||||
EventType.CALL_HANGUP == event.type -> stringProvider.getString(R.string.notice_ended_call, roomMember.displayName)
|
EventType.CALL_HANGUP == event.type -> stringProvider.getString(R.string.notice_ended_call, senderName)
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,8 +65,6 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
|
||||||
): VectorEpoxyModel<*>? {
|
): VectorEpoxyModel<*>? {
|
||||||
|
|
||||||
val eventId = event.root.eventId ?: return null
|
val eventId = event.root.eventId ?: return null
|
||||||
val roomMember = event.roomMember
|
|
||||||
val nextRoomMember = nextEvent?.roomMember
|
|
||||||
|
|
||||||
val date = event.root.localDateTime()
|
val date = event.root.localDateTime()
|
||||||
val nextDate = nextEvent?.root?.localDateTime()
|
val nextDate = nextEvent?.root?.localDateTime()
|
||||||
|
@ -75,14 +73,15 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
|
||||||
?: false
|
?: false
|
||||||
|
|
||||||
val showInformation = addDaySeparator
|
val showInformation = addDaySeparator
|
||||||
|| nextRoomMember != roomMember
|
|| event.senderAvatar != nextEvent?.senderAvatar
|
||||||
|
|| event.senderName != nextEvent?.senderName
|
||||||
|| nextEvent?.root?.type != EventType.MESSAGE
|
|| nextEvent?.root?.type != EventType.MESSAGE
|
||||||
|| isNextMessageReceivedMoreThanOneHourAgo
|
|| isNextMessageReceivedMoreThanOneHourAgo
|
||||||
|
|
||||||
val messageContent: MessageContent = event.root.content.toModel() ?: return null
|
val messageContent: MessageContent = event.root.content.toModel() ?: return null
|
||||||
val time = timelineDateFormatter.formatMessageHour(date)
|
val time = timelineDateFormatter.formatMessageHour(date)
|
||||||
val avatarUrl = roomMember?.avatarUrl
|
val avatarUrl = event.senderAvatar
|
||||||
val memberName = roomMember?.displayName ?: event.root.sender ?: ""
|
val memberName = event.senderName ?: event.root.sender ?: ""
|
||||||
val formattedMemberName = span(memberName) {
|
val formattedMemberName = span(memberName) {
|
||||||
textColor = colorProvider.getColor(getColorFor(event.root.sender ?: ""))
|
textColor = colorProvider.getColor(getColorFor(event.root.sender ?: ""))
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,15 +31,14 @@ import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem
|
||||||
class RoomHistoryVisibilityItemFactory(private val stringProvider: StringProvider) {
|
class RoomHistoryVisibilityItemFactory(private val stringProvider: StringProvider) {
|
||||||
|
|
||||||
fun create(event: TimelineEvent): NoticeItem? {
|
fun create(event: TimelineEvent): NoticeItem? {
|
||||||
val roomMember = event.roomMember ?: return null
|
val noticeText = buildNoticeText(event.root, event.senderName) ?: return null
|
||||||
val noticeText = buildNoticeText(event.root, roomMember) ?: return null
|
|
||||||
return NoticeItem_()
|
return NoticeItem_()
|
||||||
.noticeText(noticeText)
|
.noticeText(noticeText)
|
||||||
.avatarUrl(roomMember.avatarUrl)
|
.avatarUrl(event.senderAvatar)
|
||||||
.memberName(roomMember.displayName)
|
.memberName(event.senderName)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildNoticeText(event: Event, roomMember: RoomMember): CharSequence? {
|
private fun buildNoticeText(event: Event, senderName: String?): CharSequence? {
|
||||||
val content = event.content.toModel<RoomHistoryVisibilityContent>() ?: return null
|
val content = event.content.toModel<RoomHistoryVisibilityContent>() ?: return null
|
||||||
val formattedVisibility = when (content.historyVisibility) {
|
val formattedVisibility = when (content.historyVisibility) {
|
||||||
RoomHistoryVisibility.SHARED -> stringProvider.getString(R.string.notice_room_visibility_shared)
|
RoomHistoryVisibility.SHARED -> stringProvider.getString(R.string.notice_room_visibility_shared)
|
||||||
|
@ -47,7 +46,7 @@ class RoomHistoryVisibilityItemFactory(private val stringProvider: StringProvide
|
||||||
RoomHistoryVisibility.JOINED -> stringProvider.getString(R.string.notice_room_visibility_joined)
|
RoomHistoryVisibility.JOINED -> stringProvider.getString(R.string.notice_room_visibility_joined)
|
||||||
RoomHistoryVisibility.WORLD_READABLE -> stringProvider.getString(R.string.notice_room_visibility_world_readable)
|
RoomHistoryVisibility.WORLD_READABLE -> stringProvider.getString(R.string.notice_room_visibility_world_readable)
|
||||||
}
|
}
|
||||||
return stringProvider.getString(R.string.notice_made_future_room_visibility, roomMember.displayName, formattedVisibility)
|
return stringProvider.getString(R.string.notice_made_future_room_visibility, senderName, formattedVisibility)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ import im.vector.matrix.android.api.session.room.model.RoomMember
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
import im.vector.riotredesign.R
|
import im.vector.riotredesign.R
|
||||||
import im.vector.riotredesign.core.resources.StringProvider
|
import im.vector.riotredesign.core.resources.StringProvider
|
||||||
|
import im.vector.riotredesign.features.home.room.detail.timeline.helper.RoomMemberEventHelper
|
||||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem
|
import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem
|
||||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem_
|
import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem_
|
||||||
|
|
||||||
|
@ -31,18 +32,20 @@ import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem
|
||||||
class RoomMemberItemFactory(private val stringProvider: StringProvider) {
|
class RoomMemberItemFactory(private val stringProvider: StringProvider) {
|
||||||
|
|
||||||
fun create(event: TimelineEvent): NoticeItem? {
|
fun create(event: TimelineEvent): NoticeItem? {
|
||||||
val roomMember = event.roomMember ?: return null
|
val eventContent: RoomMember? = event.root.content.toModel()
|
||||||
val noticeText = buildRoomMemberNotice(event) ?: return null
|
val prevEventContent: RoomMember? = event.root.prevContent.toModel()
|
||||||
|
val noticeText = buildRoomMemberNotice(event, eventContent, prevEventContent) ?: return null
|
||||||
|
val senderAvatar = RoomMemberEventHelper.senderAvatar(eventContent, prevEventContent, event)
|
||||||
|
val senderName = RoomMemberEventHelper.senderName(eventContent, prevEventContent, event)
|
||||||
|
|
||||||
return NoticeItem_()
|
return NoticeItem_()
|
||||||
.userId(event.root.sender ?: "")
|
.userId(event.root.sender ?: "")
|
||||||
.noticeText(noticeText)
|
.noticeText(noticeText)
|
||||||
.avatarUrl(roomMember.avatarUrl)
|
.avatarUrl(senderAvatar)
|
||||||
.memberName(roomMember.displayName)
|
.memberName(senderName)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildRoomMemberNotice(event: TimelineEvent): String? {
|
private fun buildRoomMemberNotice(event: TimelineEvent, eventContent: RoomMember?, prevEventContent: RoomMember?): String? {
|
||||||
val eventContent: RoomMember? = event.root.content.toModel()
|
|
||||||
val prevEventContent: RoomMember? = event.root.prevContent.toModel()
|
|
||||||
val isMembershipEvent = prevEventContent?.membership != eventContent?.membership
|
val isMembershipEvent = prevEventContent?.membership != eventContent?.membership
|
||||||
return if (isMembershipEvent) {
|
return if (isMembershipEvent) {
|
||||||
buildMembershipNotice(event, eventContent, prevEventContent)
|
buildMembershipNotice(event, eventContent, prevEventContent)
|
||||||
|
@ -72,7 +75,7 @@ class RoomMemberItemFactory(private val stringProvider: StringProvider) {
|
||||||
displayText.append(" ")
|
displayText.append(" ")
|
||||||
stringProvider.getString(R.string.notice_avatar_changed_too)
|
stringProvider.getString(R.string.notice_avatar_changed_too)
|
||||||
} else {
|
} else {
|
||||||
stringProvider.getString(R.string.notice_avatar_url_changed, event.roomMember?.displayName)
|
stringProvider.getString(R.string.notice_avatar_url_changed, event.senderName)
|
||||||
}
|
}
|
||||||
displayText.append(displayAvatarText)
|
displayText.append(displayAvatarText)
|
||||||
}
|
}
|
||||||
|
@ -80,12 +83,12 @@ class RoomMemberItemFactory(private val stringProvider: StringProvider) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildMembershipNotice(event: TimelineEvent, eventContent: RoomMember?, prevEventContent: RoomMember?): String? {
|
private fun buildMembershipNotice(event: TimelineEvent, eventContent: RoomMember?, prevEventContent: RoomMember?): String? {
|
||||||
val senderDisplayName = event.roomMember?.displayName ?: return null
|
val senderDisplayName = event.senderName ?: event.root.sender
|
||||||
val targetDisplayName = eventContent?.displayName ?: event.root.sender
|
val targetDisplayName = eventContent?.displayName ?: event.root.sender
|
||||||
return when {
|
return when {
|
||||||
Membership.INVITE == eventContent?.membership -> {
|
Membership.INVITE == eventContent?.membership -> {
|
||||||
// TODO get userId
|
// TODO get userId
|
||||||
val selfUserId: String = ""
|
val selfUserId = ""
|
||||||
when {
|
when {
|
||||||
eventContent.thirdPartyInvite != null ->
|
eventContent.thirdPartyInvite != null ->
|
||||||
stringProvider.getString(R.string.notice_room_third_party_registered_invite,
|
stringProvider.getString(R.string.notice_room_third_party_registered_invite,
|
||||||
|
@ -106,7 +109,8 @@ class RoomMemberItemFactory(private val stringProvider: StringProvider) {
|
||||||
if (prevEventContent?.membership == Membership.INVITE) {
|
if (prevEventContent?.membership == Membership.INVITE) {
|
||||||
stringProvider.getString(R.string.notice_room_reject, senderDisplayName)
|
stringProvider.getString(R.string.notice_room_reject, senderDisplayName)
|
||||||
} else {
|
} else {
|
||||||
stringProvider.getString(R.string.notice_room_leave, senderDisplayName)
|
val leftDisplayName = RoomMemberEventHelper.senderName(eventContent, prevEventContent, event)
|
||||||
|
stringProvider.getString(R.string.notice_room_leave, leftDisplayName)
|
||||||
}
|
}
|
||||||
} else if (prevEventContent?.membership == Membership.INVITE) {
|
} else if (prevEventContent?.membership == Membership.INVITE) {
|
||||||
stringProvider.getString(R.string.notice_room_withdraw, senderDisplayName, targetDisplayName)
|
stringProvider.getString(R.string.notice_room_withdraw, senderDisplayName, targetDisplayName)
|
||||||
|
|
|
@ -29,20 +29,16 @@ class RoomNameItemFactory(private val stringProvider: StringProvider) {
|
||||||
|
|
||||||
fun create(event: TimelineEvent): NoticeItem? {
|
fun create(event: TimelineEvent): NoticeItem? {
|
||||||
|
|
||||||
val content: RoomNameContent? = event.root.content.toModel()
|
val content: RoomNameContent = event.root.content.toModel() ?: return null
|
||||||
val roomMember = event.roomMember
|
|
||||||
if (content == null || roomMember == null) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
val text = if (!TextUtils.isEmpty(content.name)) {
|
val text = if (!TextUtils.isEmpty(content.name)) {
|
||||||
stringProvider.getString(R.string.notice_room_name_changed, roomMember.displayName, content.name)
|
stringProvider.getString(R.string.notice_room_name_changed, event.senderName, content.name)
|
||||||
} else {
|
} else {
|
||||||
stringProvider.getString(R.string.notice_room_name_removed, roomMember.displayName)
|
stringProvider.getString(R.string.notice_room_name_removed, event.senderName)
|
||||||
}
|
}
|
||||||
return NoticeItem_()
|
return NoticeItem_()
|
||||||
.noticeText(text)
|
.noticeText(text)
|
||||||
.avatarUrl(roomMember.avatarUrl)
|
.avatarUrl(event.senderAvatar)
|
||||||
.memberName(roomMember.displayName)
|
.memberName(event.senderName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -28,20 +28,16 @@ class RoomTopicItemFactory(private val stringProvider: StringProvider) {
|
||||||
|
|
||||||
fun create(event: TimelineEvent): NoticeItem? {
|
fun create(event: TimelineEvent): NoticeItem? {
|
||||||
|
|
||||||
val content: RoomTopicContent? = event.root.content.toModel()
|
val content: RoomTopicContent = event.root.content.toModel() ?: return null
|
||||||
val roomMember = event.roomMember
|
|
||||||
if (content == null || roomMember == null) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
val text = if (content.topic.isNullOrEmpty()) {
|
val text = if (content.topic.isNullOrEmpty()) {
|
||||||
stringProvider.getString(R.string.notice_room_topic_removed, roomMember.displayName)
|
stringProvider.getString(R.string.notice_room_topic_removed, event.senderName)
|
||||||
} else {
|
} else {
|
||||||
stringProvider.getString(R.string.notice_room_topic_changed, roomMember.displayName, content.topic)
|
stringProvider.getString(R.string.notice_room_topic_changed, event.senderName, content.topic)
|
||||||
}
|
}
|
||||||
return NoticeItem_()
|
return NoticeItem_()
|
||||||
.noticeText(text)
|
.noticeText(text)
|
||||||
.avatarUrl(roomMember.avatarUrl)
|
.avatarUrl(event.senderAvatar)
|
||||||
.memberName(roomMember.displayName)
|
.memberName(event.senderName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -34,28 +34,18 @@ class EndlessRecyclerViewScrollListener(private val layoutManager: LinearLayoutM
|
||||||
// This happens many times a second during a scroll, so be wary of the code you place here.
|
// This happens many times a second during a scroll, so be wary of the code you place here.
|
||||||
// We are given a few useful parameters to help us work out if we need to load some more data,
|
// We are given a few useful parameters to help us work out if we need to load some more data,
|
||||||
// but first we check if we are waiting for the previous load to finish.
|
// but first we check if we are waiting for the previous load to finish.
|
||||||
|
|
||||||
override fun onScrolled(view: RecyclerView, dx: Int, dy: Int) {
|
override fun onScrolled(view: RecyclerView, dx: Int, dy: Int) {
|
||||||
val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition()
|
val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition()
|
||||||
val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition()
|
val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition()
|
||||||
val totalItemCount = layoutManager.itemCount
|
val totalItemCount = layoutManager.itemCount
|
||||||
|
|
||||||
// The minimum amount of items to have below your current scroll position
|
// We check to see if the dataset count has
|
||||||
// before loading more.
|
|
||||||
// If the total item count is zero and the previous isn't, assume the
|
|
||||||
// list is invalidated and should be reset back to initial state
|
|
||||||
if (totalItemCount < previousTotalItemCount) {
|
|
||||||
previousTotalItemCount = totalItemCount
|
|
||||||
if (totalItemCount == 0) {
|
|
||||||
loadingForwards = true
|
|
||||||
loadingBackwards = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If it’s still loading, we check to see if the dataset count has
|
|
||||||
// changed, if so we conclude it has finished loading
|
// changed, if so we conclude it has finished loading
|
||||||
if (totalItemCount > previousTotalItemCount) {
|
if (totalItemCount != previousTotalItemCount) {
|
||||||
|
previousTotalItemCount = totalItemCount
|
||||||
loadingBackwards = false
|
loadingBackwards = false
|
||||||
loadingForwards = false
|
loadingForwards = false
|
||||||
previousTotalItemCount = totalItemCount
|
|
||||||
}
|
}
|
||||||
// If it isn’t currently loading, we check to see if we have reached
|
// If it isn’t currently loading, we check to see if we have reached
|
||||||
// the visibleThreshold and need to reload more data.
|
// the visibleThreshold and need to reload more data.
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* 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.riotredesign.features.home.room.detail.timeline.helper
|
||||||
|
|
||||||
|
import im.vector.matrix.android.api.session.room.model.Membership
|
||||||
|
import im.vector.matrix.android.api.session.room.model.RoomMember
|
||||||
|
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
|
|
||||||
|
object RoomMemberEventHelper {
|
||||||
|
|
||||||
|
fun senderAvatar(eventContent: RoomMember?, prevEventContent: RoomMember?, event: TimelineEvent): String? {
|
||||||
|
return if (eventContent?.membership == Membership.LEAVE && eventContent.avatarUrl == null && prevEventContent?.avatarUrl != null) {
|
||||||
|
prevEventContent.avatarUrl
|
||||||
|
} else {
|
||||||
|
event.senderAvatar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun senderName(eventContent: RoomMember?, prevEventContent: RoomMember?, event: TimelineEvent): String? {
|
||||||
|
return if (eventContent?.membership == Membership.LEAVE && eventContent.displayName == null && prevEventContent?.displayName != null) {
|
||||||
|
prevEventContent.displayName
|
||||||
|
} else {
|
||||||
|
event.senderName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue