Timeline: fix SendState decoration + some filtering issues
This commit is contained in:
parent
2b93367165
commit
13cb81b92f
|
@ -26,12 +26,15 @@ import im.vector.app.features.home.room.detail.timeline.item.ItemWithEvents
|
|||
abstract class TimelineEmptyItem : VectorEpoxyModel<TimelineEmptyItem.Holder>(), ItemWithEvents {
|
||||
|
||||
@EpoxyAttribute lateinit var eventId: String
|
||||
@EpoxyAttribute var visible: Boolean = false
|
||||
@EpoxyAttribute var notBlank: Boolean = false
|
||||
|
||||
override fun isVisible() = false
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
super.bind(holder)
|
||||
holder.view.updateLayoutParams {
|
||||
this.height = if (visible) 1 else 0
|
||||
// Force height to 1px so scrolling works correctly
|
||||
this.height = if (notBlank) 1 else 0
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,9 +32,7 @@ import im.vector.app.core.extensions.localDateTime
|
|||
import im.vector.app.core.extensions.nextOrNull
|
||||
import im.vector.app.core.extensions.prevOrNull
|
||||
import im.vector.app.core.resources.UserPreferencesProvider
|
||||
import im.vector.app.core.utils.DebouncedClickListener
|
||||
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import im.vector.app.features.home.room.detail.RoomDetailAction
|
||||
import im.vector.app.features.home.room.detail.RoomDetailViewState
|
||||
import im.vector.app.features.home.room.detail.UnreadState
|
||||
|
@ -47,6 +45,7 @@ import im.vector.app.features.home.room.detail.timeline.helper.TimelineControlle
|
|||
import im.vector.app.features.home.room.detail.timeline.helper.TimelineEventDiffUtilCallback
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.TimelineEventVisibilityHelper
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.TimelineEventVisibilityStateChangedListener
|
||||
import im.vector.app.features.home.room.detail.timeline.factory.TimelineItemFactoryParams
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
|
||||
import im.vector.app.features.home.room.detail.timeline.item.AbsMessageItem
|
||||
import im.vector.app.features.home.room.detail.timeline.item.BasedMergedItem
|
||||
|
@ -55,7 +54,6 @@ import im.vector.app.features.home.room.detail.timeline.item.DaySeparatorItem_
|
|||
import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData
|
||||
import im.vector.app.features.home.room.detail.timeline.item.ReadReceiptData
|
||||
import im.vector.app.features.home.room.detail.timeline.item.ReadReceiptsItem
|
||||
import im.vector.app.features.home.room.detail.timeline.item.ReadReceiptsItem_
|
||||
import im.vector.app.features.home.room.detail.timeline.item.SendStateDecoration
|
||||
import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever
|
||||
import im.vector.app.features.media.ImageContentRenderer
|
||||
|
@ -337,13 +335,22 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
|
|||
return
|
||||
}
|
||||
val receiptsByEvents = getReadReceiptsByShownEvent()
|
||||
val lastSentEventWithoutReadReceipts = searchLastSentEventWithoutReadReceipts(receiptsByEvents)
|
||||
(0 until modelCache.size).forEach { position ->
|
||||
val event = currentSnapshot[position]
|
||||
val nextEvent = currentSnapshot.nextOrNull(position)
|
||||
val prevEvent = currentSnapshot.prevOrNull(position)
|
||||
val params = TimelineItemFactoryParams(
|
||||
event = event,
|
||||
prevEvent = prevEvent,
|
||||
nextEvent = nextEvent,
|
||||
highlightedEventId = eventIdToHighlight,
|
||||
lastSentEventIdWithoutReadReceipts = lastSentEventWithoutReadReceipts,
|
||||
callback = callback
|
||||
)
|
||||
// Should be build if not cached or if model should be refreshed
|
||||
if (modelCache[position] == null || modelCache[position]?.shouldTriggerBuild == true) {
|
||||
modelCache[position] = buildCacheItem(event, nextEvent, prevEvent)
|
||||
modelCache[position] = buildCacheItem(params)
|
||||
}
|
||||
val itemCachedData = modelCache[position] ?: return@forEach
|
||||
// Then update with additional models if needed
|
||||
|
@ -351,15 +358,13 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
|
|||
}
|
||||
}
|
||||
|
||||
private fun buildCacheItem(event: TimelineEvent,
|
||||
nextEvent: TimelineEvent?,
|
||||
prevEvent: TimelineEvent?
|
||||
): CacheItemData {
|
||||
private fun buildCacheItem(params: TimelineItemFactoryParams): CacheItemData {
|
||||
val event = params.event
|
||||
if (hasReachedInvite && hasUTD) {
|
||||
return CacheItemData(event.localId, event.root.eventId)
|
||||
}
|
||||
updateUTDStates(event, nextEvent)
|
||||
val eventModel = timelineItemFactory.create(event, prevEvent, nextEvent, eventIdToHighlight, callback).also {
|
||||
updateUTDStates(event, params.nextEvent)
|
||||
val eventModel = timelineItemFactory.create(params).also {
|
||||
it.id(event.localId)
|
||||
it.setOnVisibilityStateChanged(TimelineEventVisibilityStateChangedListener(callback, event))
|
||||
}
|
||||
|
@ -399,13 +404,37 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
|
|||
)
|
||||
}
|
||||
|
||||
private fun searchLastSentEventWithoutReadReceipts(receiptsByEvent: Map<String, List<ReadReceipt>>): String? {
|
||||
if (timeline?.isLive == false) {
|
||||
// If timeline is not live we don't want to show SentStatus
|
||||
return null
|
||||
}
|
||||
for (event in currentSnapshot) {
|
||||
// If there is any RR on the event, we stop searching for Sent event
|
||||
if (receiptsByEvent[event.eventId]?.isNotEmpty() == true) {
|
||||
return null
|
||||
}
|
||||
// If the event is not shown, we go to the next one
|
||||
if (!timelineEventVisibilityHelper.shouldShowEvent(event, eventIdToHighlight)) {
|
||||
continue
|
||||
}
|
||||
// If the event is sent by us, we update the holder with the eventId and stop the search
|
||||
if (event.root.senderId == session.myUserId && event.root.sendState.isSent()) {
|
||||
return event.eventId
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun getReadReceiptsByShownEvent(): Map<String, List<ReadReceipt>> {
|
||||
val receiptsByEvent = HashMap<String, MutableList<ReadReceipt>>()
|
||||
var lastShownEventId: String? = null
|
||||
val itr = currentSnapshot.listIterator(currentSnapshot.size)
|
||||
while (itr.hasPrevious()) {
|
||||
val event = itr.previous()
|
||||
val currentReadReceipts = ArrayList(event.readReceipts)
|
||||
val currentReadReceipts = ArrayList(event.readReceipts).filter {
|
||||
it.user.userId != session.myUserId
|
||||
}
|
||||
if (timelineEventVisibilityHelper.shouldShowEvent(event, eventIdToHighlight)) {
|
||||
lastShownEventId = event.eventId
|
||||
}
|
||||
|
|
|
@ -46,13 +46,11 @@ class CallItemFactory @Inject constructor(
|
|||
private val callManager: WebRtcCallManager
|
||||
) {
|
||||
|
||||
fun create(event: TimelineEvent,
|
||||
highlight: Boolean,
|
||||
callback: TimelineEventController.Callback?
|
||||
): VectorEpoxyModel<*>? {
|
||||
fun create(params: TimelineItemFactoryParams): VectorEpoxyModel<*>? {
|
||||
val event = params.event
|
||||
if (event.root.eventId == null) return null
|
||||
val roomId = event.roomId
|
||||
val informationData = messageInformationDataFactory.create(event, null, null)
|
||||
val informationData = messageInformationDataFactory.create(params)
|
||||
val callSignalingContent = event.getCallSignallingContent() ?: return null
|
||||
val callId = callSignalingContent.callId ?: return null
|
||||
val call = callManager.getCallById(callId)
|
||||
|
@ -68,8 +66,8 @@ class CallItemFactory @Inject constructor(
|
|||
callId = callId,
|
||||
callStatus = CallTileTimelineItem.CallStatus.IN_CALL,
|
||||
callKind = callKind,
|
||||
callback = callback,
|
||||
highlight = highlight,
|
||||
callback = params.callback,
|
||||
highlight = params.isHighlighted,
|
||||
informationData = informationData,
|
||||
isStillActive = call != null
|
||||
)
|
||||
|
@ -80,8 +78,8 @@ class CallItemFactory @Inject constructor(
|
|||
callId = callId,
|
||||
callStatus = CallTileTimelineItem.CallStatus.INVITED,
|
||||
callKind = callKind,
|
||||
callback = callback,
|
||||
highlight = highlight,
|
||||
callback = params.callback,
|
||||
highlight = params.isHighlighted,
|
||||
informationData = informationData,
|
||||
isStillActive = call != null
|
||||
)
|
||||
|
@ -92,8 +90,8 @@ class CallItemFactory @Inject constructor(
|
|||
callId = callId,
|
||||
callStatus = CallTileTimelineItem.CallStatus.REJECTED,
|
||||
callKind = callKind,
|
||||
callback = callback,
|
||||
highlight = highlight,
|
||||
callback = params.callback,
|
||||
highlight = params.isHighlighted,
|
||||
informationData = informationData,
|
||||
isStillActive = false
|
||||
)
|
||||
|
@ -104,8 +102,8 @@ class CallItemFactory @Inject constructor(
|
|||
callId = callId,
|
||||
callStatus = CallTileTimelineItem.CallStatus.ENDED,
|
||||
callKind = callKind,
|
||||
callback = callback,
|
||||
highlight = highlight,
|
||||
callback = params.callback,
|
||||
highlight = params.isHighlighted,
|
||||
informationData = informationData,
|
||||
isStillActive = false
|
||||
)
|
||||
|
|
|
@ -25,7 +25,6 @@ import im.vector.app.features.home.room.detail.timeline.helper.MessageInformatio
|
|||
import im.vector.app.features.home.room.detail.timeline.item.DefaultItem
|
||||
import im.vector.app.features.home.room.detail.timeline.item.DefaultItem_
|
||||
import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||
import javax.inject.Inject
|
||||
|
||||
class DefaultItemFactory @Inject constructor(private val avatarSizeProvider: AvatarSizeProvider,
|
||||
|
@ -51,16 +50,14 @@ class DefaultItemFactory @Inject constructor(private val avatarSizeProvider: Ava
|
|||
.attributes(attributes)
|
||||
}
|
||||
|
||||
fun create(event: TimelineEvent,
|
||||
highlight: Boolean,
|
||||
callback: TimelineEventController.Callback?,
|
||||
throwable: Throwable? = null): DefaultItem {
|
||||
fun create(params: TimelineItemFactoryParams, throwable: Throwable? = null): DefaultItem {
|
||||
val event = params.event
|
||||
val text = if (throwable == null) {
|
||||
stringProvider.getString(R.string.rendering_event_error_type_of_event_not_handled, event.root.getClearType())
|
||||
} else {
|
||||
stringProvider.getString(R.string.rendering_event_error_exception, event.root.eventId)
|
||||
}
|
||||
val informationData = informationDataFactory.create(event, null, null)
|
||||
return create(text, informationData, highlight, callback)
|
||||
val informationData = informationDataFactory.create(params)
|
||||
return create(text, informationData, params.isHighlighted, params.callback)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ import im.vector.app.core.epoxy.VectorEpoxyModel
|
|||
import im.vector.app.core.resources.ColorProvider
|
||||
import im.vector.app.core.resources.DrawableProvider
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.features.home.room.detail.timeline.TimelineEventController
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.AvatarSizeProvider
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.MessageInformationDataFactory
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.MessageItemAttributesFactory
|
||||
|
@ -33,7 +32,6 @@ import me.gujun.android.span.span
|
|||
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||
import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -46,11 +44,8 @@ class EncryptedItemFactory @Inject constructor(private val messageInformationDat
|
|||
private val attributesFactory: MessageItemAttributesFactory,
|
||||
private val vectorPreferences: VectorPreferences) {
|
||||
|
||||
fun create(event: TimelineEvent,
|
||||
prevEvent: TimelineEvent?,
|
||||
nextEvent: TimelineEvent?,
|
||||
highlight: Boolean,
|
||||
callback: TimelineEventController.Callback?): VectorEpoxyModel<*>? {
|
||||
fun create(params: TimelineItemFactoryParams): VectorEpoxyModel<*>? {
|
||||
val event = params.event
|
||||
event.root.eventId ?: return null
|
||||
|
||||
return when {
|
||||
|
@ -109,14 +104,14 @@ class EncryptedItemFactory @Inject constructor(private val messageInformationDat
|
|||
}
|
||||
}
|
||||
|
||||
val informationData = messageInformationDataFactory.create(event, prevEvent, nextEvent)
|
||||
val attributes = attributesFactory.create(event.root.content.toModel<EncryptedEventContent>(), informationData, callback)
|
||||
val informationData = messageInformationDataFactory.create(params)
|
||||
val attributes = attributesFactory.create(event.root.content.toModel<EncryptedEventContent>(), informationData, params.callback)
|
||||
return MessageTextItem_()
|
||||
.leftGuideline(avatarSizeProvider.leftGuideline)
|
||||
.highlighted(highlight)
|
||||
.highlighted(params.isHighlighted)
|
||||
.attributes(attributes)
|
||||
.message(spannableStr)
|
||||
.movementMethod(createLinkMovementMethod(callback))
|
||||
.movementMethod(createLinkMovementMethod(params.callback))
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ package im.vector.app.features.home.room.detail.timeline.factory
|
|||
import im.vector.app.R
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.features.home.room.detail.timeline.MessageColorProvider
|
||||
import im.vector.app.features.home.room.detail.timeline.TimelineEventController
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.AvatarSizeProvider
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.MessageInformationDataFactory
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.MessageItemAttributesFactory
|
||||
|
@ -28,7 +27,6 @@ import im.vector.app.features.home.room.detail.timeline.item.StatusTileTimelineI
|
|||
import org.matrix.android.sdk.api.extensions.orFalse
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
||||
import org.matrix.android.sdk.internal.crypto.model.event.EncryptionEventContent
|
||||
import javax.inject.Inject
|
||||
|
@ -41,15 +39,14 @@ class EncryptionItemFactory @Inject constructor(
|
|||
private val avatarSizeProvider: AvatarSizeProvider,
|
||||
private val session: Session) {
|
||||
|
||||
fun create(event: TimelineEvent,
|
||||
highlight: Boolean,
|
||||
callback: TimelineEventController.Callback?): StatusTileTimelineItem? {
|
||||
fun create(params: TimelineItemFactoryParams): StatusTileTimelineItem? {
|
||||
val event = params.event
|
||||
if (!event.root.isStateEvent()) {
|
||||
return null
|
||||
}
|
||||
val algorithm = event.root.getClearContent().toModel<EncryptionEventContent>()?.algorithm
|
||||
val informationData = informationDataFactory.create(event, null, null)
|
||||
val attributes = messageItemAttributesFactory.create(null, informationData, callback)
|
||||
val informationData = informationDataFactory.create(params)
|
||||
val attributes = messageItemAttributesFactory.create(null, informationData, params.callback)
|
||||
|
||||
val isSafeAlgorithm = algorithm == MXCRYPTO_ALGORITHM_MEGOLM
|
||||
val title: String
|
||||
|
@ -86,7 +83,7 @@ class EncryptionItemFactory @Inject constructor(
|
|||
readReceiptsCallback = attributes.readReceiptsCallback
|
||||
)
|
||||
)
|
||||
.highlighted(highlight)
|
||||
.highlighted(params.isHighlighted)
|
||||
.leftGuideline(avatarSizeProvider.leftGuideline)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ private val timelineEventVisibilityHelper: TimelineEventVisibilityHelper) {
|
|||
eventIdToHighlight: String?,
|
||||
requestModelBuild: () -> Unit,
|
||||
callback: TimelineEventController.Callback?): MergedMembershipEventsItem_? {
|
||||
val prevSameTypeEvents = timelineEventVisibilityHelper.prevSameTypeEvents(items, currentPosition, 2)
|
||||
val prevSameTypeEvents = timelineEventVisibilityHelper.prevSameTypeEvents(items, currentPosition, 2, eventIdToHighlight)
|
||||
return if (prevSameTypeEvents.isEmpty()) {
|
||||
null
|
||||
} else {
|
||||
|
|
|
@ -85,7 +85,6 @@ import org.matrix.android.sdk.api.session.room.model.message.OPTION_TYPE_BUTTONS
|
|||
import org.matrix.android.sdk.api.session.room.model.message.OPTION_TYPE_POLL
|
||||
import org.matrix.android.sdk.api.session.room.model.message.getFileName
|
||||
import org.matrix.android.sdk.api.session.room.model.message.getFileUrl
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||
import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent
|
||||
import org.matrix.android.sdk.api.util.MimeTypes
|
||||
import org.matrix.android.sdk.internal.crypto.attachments.toElementToDecrypt
|
||||
|
@ -118,15 +117,13 @@ class MessageItemFactory @Inject constructor(
|
|||
pillsPostProcessorFactory.create(roomId)
|
||||
}
|
||||
|
||||
fun create(event: TimelineEvent,
|
||||
prevEvent: TimelineEvent?,
|
||||
nextEvent: TimelineEvent?,
|
||||
highlight: Boolean,
|
||||
callback: TimelineEventController.Callback?
|
||||
): VectorEpoxyModel<*>? {
|
||||
fun create(params: TimelineItemFactoryParams): VectorEpoxyModel<*>? {
|
||||
val event = params.event
|
||||
val highlight = params.isHighlighted
|
||||
val callback = params.callback
|
||||
event.root.eventId ?: return null
|
||||
roomId = event.roomId
|
||||
val informationData = messageInformationDataFactory.create(event, prevEvent, nextEvent)
|
||||
val informationData = messageInformationDataFactory.create(params)
|
||||
if (event.root.isRedacted()) {
|
||||
// message is redacted
|
||||
val attributes = messageItemAttributesFactory.create(null, informationData, callback)
|
||||
|
@ -142,7 +139,7 @@ class MessageItemFactory @Inject constructor(
|
|||
|| event.isEncrypted() && event.root.content.toModel<EncryptedEventContent>()?.relatesTo?.type == RelationType.REPLACE
|
||||
) {
|
||||
// This is an edit event, we should display it when debugging as a notice event
|
||||
return noticeItemFactory.create(event, highlight, callback)
|
||||
return noticeItemFactory.create(params)
|
||||
}
|
||||
val attributes = messageItemAttributesFactory.create(messageContent, informationData, callback)
|
||||
|
||||
|
@ -158,7 +155,7 @@ class MessageItemFactory @Inject constructor(
|
|||
is MessageAudioContent -> buildAudioMessageItem(messageContent, informationData, highlight, attributes)
|
||||
is MessageVerificationRequestContent -> buildVerificationRequestMessageItem(messageContent, informationData, highlight, callback, attributes)
|
||||
is MessageOptionsContent -> buildOptionsMessageItem(messageContent, informationData, highlight, callback, attributes)
|
||||
is MessagePollResponseContent -> noticeItemFactory.create(event, highlight, callback)
|
||||
is MessagePollResponseContent -> noticeItemFactory.create(params)
|
||||
else -> buildNotHandledMessageItem(messageContent, informationData, highlight, callback, attributes)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,13 +17,11 @@
|
|||
package im.vector.app.features.home.room.detail.timeline.factory
|
||||
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import im.vector.app.features.home.room.detail.timeline.TimelineEventController
|
||||
import im.vector.app.features.home.room.detail.timeline.format.NoticeEventFormatter
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.AvatarSizeProvider
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.MessageInformationDataFactory
|
||||
import im.vector.app.features.home.room.detail.timeline.item.NoticeItem
|
||||
import im.vector.app.features.home.room.detail.timeline.item.NoticeItem_
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||
import javax.inject.Inject
|
||||
|
||||
class NoticeItemFactory @Inject constructor(private val eventFormatter: NoticeEventFormatter,
|
||||
|
@ -31,24 +29,23 @@ class NoticeItemFactory @Inject constructor(private val eventFormatter: NoticeEv
|
|||
private val informationDataFactory: MessageInformationDataFactory,
|
||||
private val avatarSizeProvider: AvatarSizeProvider) {
|
||||
|
||||
fun create(event: TimelineEvent,
|
||||
highlight: Boolean,
|
||||
callback: TimelineEventController.Callback?): NoticeItem? {
|
||||
fun create(params: TimelineItemFactoryParams): NoticeItem? {
|
||||
val event = params.event
|
||||
val formattedText = eventFormatter.format(event) ?: return null
|
||||
val informationData = informationDataFactory.create(event, null, null)
|
||||
val informationData = informationDataFactory.create(params)
|
||||
val attributes = NoticeItem.Attributes(
|
||||
avatarRenderer = avatarRenderer,
|
||||
informationData = informationData,
|
||||
noticeText = formattedText,
|
||||
itemLongClickListener = { view ->
|
||||
callback?.onEventLongClicked(informationData, null, view) ?: false
|
||||
params.callback?.onEventLongClicked(informationData, null, view) ?: false
|
||||
},
|
||||
readReceiptsCallback = callback,
|
||||
avatarClickListener = { callback?.onAvatarClicked(informationData) }
|
||||
readReceiptsCallback = params.callback,
|
||||
avatarClickListener = { params.callback?.onAvatarClicked(informationData) }
|
||||
)
|
||||
return NoticeItem_()
|
||||
.leftGuideline(avatarSizeProvider.leftGuideline)
|
||||
.highlighted(highlight)
|
||||
.highlighted(params.isHighlighted)
|
||||
.attributes(attributes)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,27 +22,21 @@ import im.vector.app.features.home.room.detail.timeline.TimelineEventController
|
|||
import im.vector.app.features.home.room.detail.timeline.item.ReadReceiptData
|
||||
import im.vector.app.features.home.room.detail.timeline.item.ReadReceiptsItem
|
||||
import im.vector.app.features.home.room.detail.timeline.item.ReadReceiptsItem_
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.room.model.ReadReceipt
|
||||
import javax.inject.Inject
|
||||
|
||||
class ReadReceiptsItemFactory @Inject constructor(private val session: Session,
|
||||
private val avatarRenderer: AvatarRenderer) {
|
||||
class ReadReceiptsItemFactory @Inject constructor(private val avatarRenderer: AvatarRenderer) {
|
||||
|
||||
fun create(eventId: String, readReceipts: List<ReadReceipt>, callback: TimelineEventController.Callback?): ReadReceiptsItem? {
|
||||
if (readReceipts.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
val readReceiptsData = readReceipts
|
||||
.asSequence()
|
||||
.filter {
|
||||
it.user.userId != session.myUserId
|
||||
}
|
||||
.map {
|
||||
ReadReceiptData(it.user.userId, it.user.avatarUrl, it.user.displayName, it.originServerTs)
|
||||
}
|
||||
.toList()
|
||||
|
||||
if (readReceiptsData.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
return ReadReceiptsItem_()
|
||||
.id("read_receipts_$eventId")
|
||||
.eventId(eventId)
|
||||
|
|
|
@ -20,13 +20,11 @@ import im.vector.app.R
|
|||
import im.vector.app.core.epoxy.VectorEpoxyModel
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.core.resources.UserPreferencesProvider
|
||||
import im.vector.app.features.home.room.detail.timeline.TimelineEventController
|
||||
import im.vector.app.features.home.room.detail.timeline.item.RoomCreateItem_
|
||||
import me.gujun.android.span.span
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||
import javax.inject.Inject
|
||||
|
||||
class RoomCreateItemFactory @Inject constructor(private val stringProvider: StringProvider,
|
||||
|
@ -34,25 +32,26 @@ class RoomCreateItemFactory @Inject constructor(private val stringProvider: Stri
|
|||
private val session: Session,
|
||||
private val noticeItemFactory: NoticeItemFactory) {
|
||||
|
||||
fun create(event: TimelineEvent, callback: TimelineEventController.Callback?): VectorEpoxyModel<*>? {
|
||||
fun create(params: TimelineItemFactoryParams): VectorEpoxyModel<*>? {
|
||||
val event = params.event
|
||||
val createRoomContent = event.root.getClearContent().toModel<RoomCreateContent>() ?: return null
|
||||
val predecessorId = createRoomContent.predecessor?.roomId ?: return defaultRendering(event, callback)
|
||||
val predecessorId = createRoomContent.predecessor?.roomId ?: return defaultRendering(params)
|
||||
val roomLink = session.permalinkService().createRoomPermalink(predecessorId) ?: return null
|
||||
val text = span {
|
||||
+stringProvider.getString(R.string.room_tombstone_continuation_description)
|
||||
+"\n"
|
||||
span(stringProvider.getString(R.string.room_tombstone_predecessor_link)) {
|
||||
textDecorationLine = "underline"
|
||||
onClick = { callback?.onRoomCreateLinkClicked(roomLink) }
|
||||
onClick = { params.callback?.onRoomCreateLinkClicked(roomLink) }
|
||||
}
|
||||
}
|
||||
return RoomCreateItem_()
|
||||
.text(text)
|
||||
}
|
||||
|
||||
private fun defaultRendering(event: TimelineEvent, callback: TimelineEventController.Callback?): VectorEpoxyModel<*>? {
|
||||
private fun defaultRendering(params: TimelineItemFactoryParams): VectorEpoxyModel<*>? {
|
||||
return if (userPreferencesProvider.shouldShowHiddenEvents()) {
|
||||
noticeItemFactory.create(event, false, callback)
|
||||
noticeItemFactory.create(params)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
|
|
@ -19,8 +19,6 @@ package im.vector.app.features.home.room.detail.timeline.factory
|
|||
import im.vector.app.core.epoxy.TimelineEmptyItem
|
||||
import im.vector.app.core.epoxy.TimelineEmptyItem_
|
||||
import im.vector.app.core.epoxy.VectorEpoxyModel
|
||||
import im.vector.app.core.resources.UserPreferencesProvider
|
||||
import im.vector.app.features.home.room.detail.timeline.TimelineEventController
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.TimelineEventVisibilityHelper
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||
|
@ -41,20 +39,16 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me
|
|||
/**
|
||||
* Reminder: nextEvent is older and prevEvent is newer.
|
||||
*/
|
||||
fun create(event: TimelineEvent,
|
||||
prevEvent: TimelineEvent?,
|
||||
nextEvent: TimelineEvent?,
|
||||
eventIdToHighlight: String?,
|
||||
callback: TimelineEventController.Callback?): VectorEpoxyModel<*> {
|
||||
val highlight = event.root.eventId == eventIdToHighlight
|
||||
fun create(params: TimelineItemFactoryParams): VectorEpoxyModel<*> {
|
||||
val event = params.event
|
||||
val computedModel = try {
|
||||
if (!timelineEventVisibilityHelper.shouldShowEvent(event, eventIdToHighlight)) {
|
||||
return buildEmptyItem(event, prevEvent, eventIdToHighlight)
|
||||
if (!timelineEventVisibilityHelper.shouldShowEvent(event, params.highlightedEventId)) {
|
||||
return buildEmptyItem(event, params.prevEvent, params.highlightedEventId)
|
||||
}
|
||||
when (event.root.getClearType()) {
|
||||
// Message items
|
||||
// Message itemsX
|
||||
EventType.STICKER,
|
||||
EventType.MESSAGE -> messageItemFactory.create(event, prevEvent, nextEvent, highlight, callback)
|
||||
EventType.MESSAGE -> messageItemFactory.create(params)
|
||||
EventType.STATE_ROOM_TOMBSTONE,
|
||||
EventType.STATE_ROOM_NAME,
|
||||
EventType.STATE_ROOM_TOPIC,
|
||||
|
@ -78,49 +72,49 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me
|
|||
EventType.CALL_SELECT_ANSWER,
|
||||
EventType.CALL_NEGOTIATE,
|
||||
EventType.REACTION,
|
||||
EventType.STATE_ROOM_POWER_LEVELS -> noticeItemFactory.create(event, highlight, callback)
|
||||
EventType.STATE_ROOM_POWER_LEVELS -> noticeItemFactory.create(params)
|
||||
EventType.STATE_ROOM_WIDGET_LEGACY,
|
||||
EventType.STATE_ROOM_WIDGET -> widgetItemFactory.create(event, highlight, callback)
|
||||
EventType.STATE_ROOM_ENCRYPTION -> encryptionItemFactory.create(event, highlight, callback)
|
||||
EventType.STATE_ROOM_WIDGET -> widgetItemFactory.create(params)
|
||||
EventType.STATE_ROOM_ENCRYPTION -> encryptionItemFactory.create(params)
|
||||
// State room create
|
||||
EventType.STATE_ROOM_CREATE -> roomCreateItemFactory.create(event, callback)
|
||||
EventType.STATE_ROOM_CREATE -> roomCreateItemFactory.create(params)
|
||||
// Calls
|
||||
EventType.CALL_INVITE,
|
||||
EventType.CALL_HANGUP,
|
||||
EventType.CALL_REJECT,
|
||||
EventType.CALL_ANSWER -> callItemFactory.create(event, highlight, callback)
|
||||
EventType.CALL_ANSWER -> callItemFactory.create(params)
|
||||
// Crypto
|
||||
EventType.ENCRYPTED -> {
|
||||
if (event.root.isRedacted()) {
|
||||
// Redacted event, let the MessageItemFactory handle it
|
||||
messageItemFactory.create(event, prevEvent, nextEvent, highlight, callback)
|
||||
messageItemFactory.create(params)
|
||||
} else {
|
||||
encryptedItemFactory.create(event, prevEvent, nextEvent, highlight, callback)
|
||||
encryptedItemFactory.create(params)
|
||||
}
|
||||
}
|
||||
EventType.KEY_VERIFICATION_CANCEL,
|
||||
EventType.KEY_VERIFICATION_DONE -> {
|
||||
verificationConclusionItemFactory.create(event, highlight, callback)
|
||||
verificationConclusionItemFactory.create(params)
|
||||
}
|
||||
// Unhandled event types
|
||||
else -> {
|
||||
// Should only happen when shouldShowHiddenEvents() settings is ON
|
||||
Timber.v("Type ${event.root.getClearType()} not handled")
|
||||
defaultItemFactory.create(event, highlight, callback)
|
||||
defaultItemFactory.create(params)
|
||||
}
|
||||
}
|
||||
} catch (throwable: Throwable) {
|
||||
Timber.e(throwable, "failed to create message item")
|
||||
defaultItemFactory.create(event, highlight, callback, throwable)
|
||||
defaultItemFactory.create(params, throwable)
|
||||
}
|
||||
return computedModel ?: buildEmptyItem(event, prevEvent, eventIdToHighlight)
|
||||
return computedModel ?: buildEmptyItem(event, params.prevEvent, params.highlightedEventId)
|
||||
}
|
||||
|
||||
private fun buildEmptyItem(timelineEvent: TimelineEvent, prevEvent: TimelineEvent?, eventIdToHighlight: String?): TimelineEmptyItem {
|
||||
val makesEmptyItemVisible = prevEvent == null || timelineEventVisibilityHelper.shouldShowEvent(prevEvent, eventIdToHighlight)
|
||||
private fun buildEmptyItem(timelineEvent: TimelineEvent, prevEvent: TimelineEvent?, highlightedEventId: String?): TimelineEmptyItem {
|
||||
val isNotBlank = prevEvent == null || timelineEventVisibilityHelper.shouldShowEvent(prevEvent, highlightedEventId)
|
||||
return TimelineEmptyItem_()
|
||||
.id(timelineEvent.localId)
|
||||
.eventId(timelineEvent.eventId)
|
||||
.visible(makesEmptyItemVisible)
|
||||
.notBlank(isNotBlank)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (c) 2021 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.app.features.home.room.detail.timeline.factory
|
||||
|
||||
import im.vector.app.features.home.room.detail.timeline.TimelineEventController
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||
|
||||
data class TimelineItemFactoryParams(
|
||||
val event: TimelineEvent,
|
||||
val prevEvent: TimelineEvent? = null,
|
||||
val nextEvent: TimelineEvent? = null,
|
||||
val highlightedEventId: String? = null ,
|
||||
val lastSentEventIdWithoutReadReceipts: String? = null ,
|
||||
val callback: TimelineEventController.Callback? = null
|
||||
) {
|
||||
|
||||
val isHighlighted: Boolean
|
||||
get() = highlightedEventId == event.eventId
|
||||
}
|
|
@ -20,7 +20,6 @@ import im.vector.app.core.epoxy.VectorEpoxyModel
|
|||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.core.resources.UserPreferencesProvider
|
||||
import im.vector.app.features.home.room.detail.timeline.MessageColorProvider
|
||||
import im.vector.app.features.home.room.detail.timeline.TimelineEventController
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.AvatarSizeProvider
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.MessageInformationDataFactory
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.MessageItemAttributesFactory
|
||||
|
@ -35,7 +34,6 @@ import org.matrix.android.sdk.api.session.events.model.RelationType
|
|||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageRelationContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationCancelContent
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
|
@ -54,37 +52,35 @@ class VerificationItemFactory @Inject constructor(
|
|||
private val session: Session
|
||||
) {
|
||||
|
||||
fun create(event: TimelineEvent,
|
||||
highlight: Boolean,
|
||||
callback: TimelineEventController.Callback?
|
||||
): VectorEpoxyModel<*>? {
|
||||
fun create(params: TimelineItemFactoryParams): VectorEpoxyModel<*>? {
|
||||
val event = params.event
|
||||
if (event.root.eventId == null) return null
|
||||
|
||||
val relContent: MessageRelationContent = event.root.content.toModel()
|
||||
?: event.root.getClearContent().toModel()
|
||||
?: return ignoredConclusion(event, highlight, callback)
|
||||
?: return ignoredConclusion(params)
|
||||
|
||||
if (relContent.relatesTo?.type != RelationType.REFERENCE) return ignoredConclusion(event, highlight, callback)
|
||||
if (relContent.relatesTo?.type != RelationType.REFERENCE) return ignoredConclusion(params)
|
||||
val refEventId = relContent.relatesTo?.eventId
|
||||
?: return ignoredConclusion(event, highlight, callback)
|
||||
?: return ignoredConclusion(params)
|
||||
|
||||
// If we cannot find the referenced request we do not display the done event
|
||||
val refEvent = session.getRoom(event.root.roomId ?: "")?.getTimeLineEvent(refEventId)
|
||||
?: return ignoredConclusion(event, highlight, callback)
|
||||
?: return ignoredConclusion(params)
|
||||
|
||||
// If it's not a request ignore this event
|
||||
// if (refEvent.root.getClearContent().toModel<MessageVerificationRequestContent>() == null) return ignoredConclusion(event, highlight, callback)
|
||||
|
||||
val referenceInformationData = messageInformationDataFactory.create(refEvent, null, null)
|
||||
val referenceInformationData = messageInformationDataFactory.create(TimelineItemFactoryParams(refEvent))
|
||||
|
||||
val informationData = messageInformationDataFactory.create(event, null, null)
|
||||
val attributes = messageItemAttributesFactory.create(null, informationData, callback)
|
||||
val informationData = messageInformationDataFactory.create(params)
|
||||
val attributes = messageItemAttributesFactory.create(null, informationData,params.callback)
|
||||
|
||||
when (event.root.getClearType()) {
|
||||
EventType.KEY_VERIFICATION_CANCEL -> {
|
||||
// Is the request referenced is actually really cancelled?
|
||||
val cancelContent = event.root.getClearContent().toModel<MessageVerificationCancelContent>()
|
||||
?: return ignoredConclusion(event, highlight, callback)
|
||||
?: return ignoredConclusion(params)
|
||||
|
||||
when (safeValueOf(cancelContent.code)) {
|
||||
CancelCode.MismatchedCommitment,
|
||||
|
@ -107,22 +103,22 @@ class VerificationItemFactory @Inject constructor(
|
|||
readReceiptsCallback = attributes.readReceiptsCallback
|
||||
)
|
||||
)
|
||||
.highlighted(highlight)
|
||||
.highlighted(params.isHighlighted)
|
||||
.leftGuideline(avatarSizeProvider.leftGuideline)
|
||||
}
|
||||
else -> return ignoredConclusion(event, highlight, callback)
|
||||
else -> return ignoredConclusion(params)
|
||||
}
|
||||
}
|
||||
EventType.KEY_VERIFICATION_DONE -> {
|
||||
// Is the request referenced is actually really completed?
|
||||
if (referenceInformationData.referencesInfoData?.verificationStatus != VerificationState.DONE) {
|
||||
return ignoredConclusion(event, highlight, callback)
|
||||
return ignoredConclusion(params)
|
||||
}
|
||||
// We only tale the one sent by me
|
||||
|
||||
if (informationData.sentByMe) {
|
||||
// We only display the done sent by the other user, the done send by me is ignored
|
||||
return ignoredConclusion(event, highlight, callback)
|
||||
return ignoredConclusion(params)
|
||||
}
|
||||
return StatusTileTimelineItem_()
|
||||
.attributes(
|
||||
|
@ -140,18 +136,15 @@ class VerificationItemFactory @Inject constructor(
|
|||
readReceiptsCallback = attributes.readReceiptsCallback
|
||||
)
|
||||
)
|
||||
.highlighted(highlight)
|
||||
.highlighted(params.isHighlighted)
|
||||
.leftGuideline(avatarSizeProvider.leftGuideline)
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun ignoredConclusion(event: TimelineEvent,
|
||||
highlight: Boolean,
|
||||
callback: TimelineEventController.Callback?
|
||||
): VectorEpoxyModel<*>? {
|
||||
if (userPreferencesProvider.shouldShowHiddenEvents()) return noticeItemFactory.create(event, highlight, callback)
|
||||
private fun ignoredConclusion(params: TimelineItemFactoryParams): VectorEpoxyModel<*>? {
|
||||
if (userPreferencesProvider.shouldShowHiddenEvents()) return noticeItemFactory.create(params)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ import im.vector.app.ActiveSessionDataSource
|
|||
import im.vector.app.R
|
||||
import im.vector.app.core.epoxy.VectorEpoxyModel
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.features.home.room.detail.timeline.TimelineEventController
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.AvatarSizeProvider
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.MessageInformationDataFactory
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.MessageItemAttributesFactory
|
||||
|
@ -29,7 +28,6 @@ import im.vector.app.features.home.room.detail.timeline.item.WidgetTileTimelineI
|
|||
import org.matrix.android.sdk.api.extensions.orFalse
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||
import org.matrix.android.sdk.api.session.widgets.model.WidgetContent
|
||||
import org.matrix.android.sdk.api.session.widgets.model.WidgetType
|
||||
import javax.inject.Inject
|
||||
|
@ -47,25 +45,24 @@ class WidgetItemFactory @Inject constructor(
|
|||
|
||||
private fun Event.isSentByCurrentUser() = senderId != null && senderId == currentUserId
|
||||
|
||||
fun create(event: TimelineEvent,
|
||||
highlight: Boolean,
|
||||
callback: TimelineEventController.Callback?): VectorEpoxyModel<*>? {
|
||||
fun create(params: TimelineItemFactoryParams): VectorEpoxyModel<*>? {
|
||||
val event = params.event
|
||||
val widgetContent: WidgetContent = event.root.getClearContent().toModel() ?: return null
|
||||
val previousWidgetContent: WidgetContent? = event.root.resolvedPrevContent().toModel()
|
||||
|
||||
return when (WidgetType.fromString(widgetContent.type ?: previousWidgetContent?.type ?: "")) {
|
||||
WidgetType.Jitsi -> createJitsiItem(event, callback, widgetContent, previousWidgetContent)
|
||||
WidgetType.Jitsi -> createJitsiItem(params, widgetContent, previousWidgetContent)
|
||||
// There is lot of other widget types we could improve here
|
||||
else -> noticeItemFactory.create(event, highlight, callback)
|
||||
else -> noticeItemFactory.create(params)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createJitsiItem(timelineEvent: TimelineEvent,
|
||||
callback: TimelineEventController.Callback?,
|
||||
private fun createJitsiItem(params: TimelineItemFactoryParams,
|
||||
widgetContent: WidgetContent,
|
||||
previousWidgetContent: WidgetContent?): VectorEpoxyModel<*> {
|
||||
val informationData = informationDataFactory.create(timelineEvent, null, null)
|
||||
val attributes = messageItemAttributesFactory.create(null, informationData, callback)
|
||||
val timelineEvent = params.event
|
||||
val informationData = informationDataFactory.create(params)
|
||||
val attributes = messageItemAttributesFactory.create(null, informationData, params.callback)
|
||||
|
||||
val disambiguatedDisplayName = timelineEvent.senderInfo.disambiguatedDisplayName
|
||||
val message = if (widgetContent.isActive()) {
|
||||
|
|
|
@ -19,11 +19,11 @@ package im.vector.app.features.home.room.detail.timeline.helper
|
|||
import im.vector.app.core.date.DateFormatKind
|
||||
import im.vector.app.core.date.VectorDateFormatter
|
||||
import im.vector.app.core.extensions.localDateTime
|
||||
import im.vector.app.features.home.room.detail.timeline.factory.TimelineItemFactoryParams
|
||||
import im.vector.app.features.home.room.detail.timeline.item.E2EDecoration
|
||||
import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData
|
||||
import im.vector.app.features.home.room.detail.timeline.item.PollResponseData
|
||||
import im.vector.app.features.home.room.detail.timeline.item.ReactionInfoData
|
||||
import im.vector.app.features.home.room.detail.timeline.item.ReadReceiptData
|
||||
import im.vector.app.features.home.room.detail.timeline.item.ReferencesInfoData
|
||||
import im.vector.app.features.home.room.detail.timeline.item.SendStateDecoration
|
||||
import im.vector.app.features.settings.VectorPreferences
|
||||
|
@ -51,9 +51,10 @@ class MessageInformationDataFactory @Inject constructor(private val session: Ses
|
|||
private val dateFormatter: VectorDateFormatter,
|
||||
private val vectorPreferences: VectorPreferences) {
|
||||
|
||||
fun create(event: TimelineEvent, prevEvent: TimelineEvent?, nextEvent: TimelineEvent?): MessageInformationData {
|
||||
// Non nullability has been tested before
|
||||
val eventId = event.root.eventId!!
|
||||
fun create(params: TimelineItemFactoryParams): MessageInformationData {
|
||||
val event = params.event
|
||||
val nextEvent = params.nextEvent
|
||||
val eventId = event.eventId
|
||||
|
||||
val date = event.root.localDateTime()
|
||||
val nextDate = nextEvent?.root?.localDateTime()
|
||||
|
@ -76,9 +77,8 @@ class MessageInformationDataFactory @Inject constructor(private val session: Ses
|
|||
val isSentByMe = event.root.senderId == session.myUserId
|
||||
val sendStateDecoration = if (isSentByMe) {
|
||||
getSendStateDecoration(
|
||||
eventSendState = event.root.sendState,
|
||||
prevEventSendState = prevEvent?.root?.sendState,
|
||||
anyReadReceipts = event.readReceipts.any { it.user.userId != session.myUserId },
|
||||
event = event,
|
||||
lastSentEventWithoutReadReceipts = params.lastSentEventIdWithoutReadReceipts,
|
||||
isMedia = event.root.isAttachmentMessage()
|
||||
)
|
||||
} else {
|
||||
|
@ -122,15 +122,15 @@ class MessageInformationDataFactory @Inject constructor(private val session: Ses
|
|||
)
|
||||
}
|
||||
|
||||
private fun getSendStateDecoration(eventSendState: SendState,
|
||||
prevEventSendState: SendState?,
|
||||
anyReadReceipts: Boolean,
|
||||
private fun getSendStateDecoration(event: TimelineEvent,
|
||||
lastSentEventWithoutReadReceipts: String?,
|
||||
isMedia: Boolean): SendStateDecoration {
|
||||
val eventSendState = event.root.sendState
|
||||
return if (eventSendState.isSending()) {
|
||||
if (isMedia) SendStateDecoration.SENDING_MEDIA else SendStateDecoration.SENDING_NON_MEDIA
|
||||
} else if (eventSendState.hasFailed()) {
|
||||
SendStateDecoration.FAILED
|
||||
} else if (eventSendState.isSent() && !prevEventSendState?.isSent().orFalse() && !anyReadReceipts) {
|
||||
} else if (lastSentEventWithoutReadReceipts == event.eventId) {
|
||||
SendStateDecoration.SENT
|
||||
} else {
|
||||
SendStateDecoration.NONE
|
||||
|
|
|
@ -19,7 +19,6 @@ package im.vector.app.features.home.room.detail.timeline.helper
|
|||
import com.airbnb.epoxy.EpoxyModel
|
||||
import com.airbnb.epoxy.VisibilityState
|
||||
import im.vector.app.core.epoxy.LoadingItem_
|
||||
import im.vector.app.core.epoxy.TimelineEmptyItem
|
||||
import im.vector.app.core.epoxy.TimelineEmptyItem_
|
||||
import im.vector.app.core.resources.UserPreferencesProvider
|
||||
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
||||
|
@ -29,7 +28,6 @@ import im.vector.app.features.home.room.detail.timeline.item.CallTileTimelineIte
|
|||
import im.vector.app.features.home.room.detail.timeline.item.DaySeparatorItem
|
||||
import im.vector.app.features.home.room.detail.timeline.item.ItemWithEvents
|
||||
import im.vector.app.features.home.room.detail.timeline.item.TimelineReadMarkerItem_
|
||||
import im.vector.app.features.settings.VectorPreferences
|
||||
import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
||||
import kotlin.reflect.KMutableProperty0
|
||||
|
||||
|
@ -67,30 +65,29 @@ class TimelineControllerInterceptorHelper(private val positionOfReadMarker: KMut
|
|||
|
||||
// Then iterate on models so we have the exact positions in the adapter
|
||||
modelsIterator.forEach { epoxyModel ->
|
||||
if(epoxyModel !is TimelineEmptyItem){
|
||||
atLeastOneVisibleItemSinceLastDaySeparator = true
|
||||
atLeastOneVisibleItemsBeforeReadMarker = true
|
||||
}
|
||||
if (epoxyModel is ItemWithEvents) {
|
||||
if (epoxyModel.isVisible()) {
|
||||
atLeastOneVisibleItemSinceLastDaySeparator = true
|
||||
atLeastOneVisibleItemsBeforeReadMarker = true
|
||||
}
|
||||
epoxyModel.getEventIds().forEach { eventId ->
|
||||
adapterPositionMapping[eventId] = index
|
||||
if (eventId == firstUnreadEventId && atLeastOneVisibleItemsBeforeReadMarker) {
|
||||
if (eventId == firstUnreadEventId && atLeastOneVisibleItemsBeforeReadMarker && epoxyModel.canAppendReadMarker()) {
|
||||
modelsIterator.addReadMarkerItem(callback)
|
||||
index++
|
||||
positionOfReadMarker.set(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
if(epoxyModel is DaySeparatorItem){
|
||||
if(!atLeastOneVisibleItemSinceLastDaySeparator){
|
||||
if (epoxyModel is DaySeparatorItem) {
|
||||
if (!atLeastOneVisibleItemSinceLastDaySeparator) {
|
||||
modelsIterator.remove()
|
||||
return@forEach
|
||||
}
|
||||
atLeastOneVisibleItemSinceLastDaySeparator = false
|
||||
}
|
||||
else if (epoxyModel is CallTileTimelineItem) {
|
||||
} else if (epoxyModel is CallTileTimelineItem) {
|
||||
val hasBeenRemoved = modelsIterator.removeCallItemIfNeeded(epoxyModel, callIds, showHiddenEvents)
|
||||
if(!hasBeenRemoved){
|
||||
if (!hasBeenRemoved) {
|
||||
atLeastOneVisibleItemSinceLastDaySeparator = true
|
||||
}
|
||||
}
|
||||
|
@ -126,7 +123,7 @@ class TimelineControllerInterceptorHelper(private val positionOfReadMarker: KMut
|
|||
val emptyItem = TimelineEmptyItem_()
|
||||
.id(epoxyModel.id())
|
||||
.eventId(epoxyModel.attributes.informationData.eventId)
|
||||
.visible(false)
|
||||
.notBlank(false)
|
||||
add(emptyItem)
|
||||
}
|
||||
callIds.add(callId)
|
||||
|
|
|
@ -29,7 +29,7 @@ import javax.inject.Inject
|
|||
|
||||
class TimelineEventVisibilityHelper @Inject constructor(private val userPreferencesProvider: UserPreferencesProvider) {
|
||||
|
||||
fun nextSameTypeEvents(timelineEvents: List<TimelineEvent>, index: Int, minSize: Int): List<TimelineEvent> {
|
||||
fun nextSameTypeEvents(timelineEvents: List<TimelineEvent>, index: Int, minSize: Int, eventIdToHighlight: String?): List<TimelineEvent> {
|
||||
if (index >= timelineEvents.size - 1) {
|
||||
return emptyList()
|
||||
}
|
||||
|
@ -51,30 +51,30 @@ class TimelineEventVisibilityHelper @Inject constructor(private val userPreferen
|
|||
} else {
|
||||
nextSameDayEvents.subList(0, indexOfFirstDifferentEventType)
|
||||
}
|
||||
val filteredSameTypeEvents = sameTypeEvents.filter { shouldShowEvent(it)}
|
||||
val filteredSameTypeEvents = sameTypeEvents.filter { shouldShowEvent(it, eventIdToHighlight)}
|
||||
if (filteredSameTypeEvents.size < minSize) {
|
||||
return emptyList()
|
||||
}
|
||||
return filteredSameTypeEvents
|
||||
}
|
||||
|
||||
fun prevSameTypeEvents(timelineEvents: List<TimelineEvent>, index: Int, minSize: Int): List<TimelineEvent> {
|
||||
fun prevSameTypeEvents(timelineEvents: List<TimelineEvent>, index: Int, minSize: Int, eventIdToHighlight: String?): List<TimelineEvent> {
|
||||
val prevSub = timelineEvents.subList(0, index + 1)
|
||||
return prevSub
|
||||
.reversed()
|
||||
.let {
|
||||
nextSameTypeEvents(it, 0, minSize)
|
||||
nextSameTypeEvents(it, 0, minSize, eventIdToHighlight)
|
||||
}
|
||||
.reversed()
|
||||
}
|
||||
|
||||
fun shouldShowEvent(timelineEvent: TimelineEvent, highlightEventId: String? = null): Boolean {
|
||||
fun shouldShowEvent(timelineEvent: TimelineEvent, highlightedEventId: String?): Boolean {
|
||||
// If show hidden events is true we should always display something
|
||||
if (userPreferencesProvider.shouldShowHiddenEvents()) {
|
||||
return true
|
||||
}
|
||||
// We always show highlighted event
|
||||
if (timelineEvent.eventId == highlightEventId) {
|
||||
if (timelineEvent.eventId == highlightedEventId) {
|
||||
return true
|
||||
}
|
||||
if (!timelineEvent.isDisplayable()) {
|
||||
|
|
|
@ -44,6 +44,7 @@ abstract class BaseEventItem<H : BaseEventItem.BaseHolder> : VectorEpoxyModel<H>
|
|||
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
|
||||
lateinit var dimensionConverter: DimensionConverter
|
||||
|
||||
|
||||
@CallSuper
|
||||
override fun bind(holder: H) {
|
||||
super.bind(holder)
|
||||
|
|
|
@ -22,4 +22,9 @@ interface ItemWithEvents {
|
|||
* Will generally get only one, but it handles the merged items.
|
||||
*/
|
||||
fun getEventIds(): List<String>
|
||||
|
||||
fun canAppendReadMarker(): Boolean = true
|
||||
|
||||
fun isVisible(): Boolean = true
|
||||
|
||||
}
|
||||
|
|
|
@ -33,6 +33,8 @@ abstract class ReadReceiptsItem : EpoxyModelWithHolder<ReadReceiptsItem.Holder>(
|
|||
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) lateinit var avatarRenderer: AvatarRenderer
|
||||
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) lateinit var clickListener: View.OnClickListener
|
||||
|
||||
override fun canAppendReadMarker(): Boolean = false
|
||||
|
||||
override fun getEventIds(): List<String> = listOf(eventId)
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
|
|
Loading…
Reference in New Issue