Merge pull request #3520 from vector-im/feature/fga/timeline_virtual_room
Feature/fga/timeline virtual room
This commit is contained in:
commit
f5ecaa0339
|
@ -0,0 +1 @@
|
||||||
|
VoIP: Merge virtual room timeline in corresponding native room (call events only).
|
|
@ -27,11 +27,21 @@ import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
||||||
class CallUserMapper(private val session: Session, private val protocolsChecker: CallProtocolsChecker) {
|
class CallUserMapper(private val session: Session, private val protocolsChecker: CallProtocolsChecker) {
|
||||||
|
|
||||||
fun nativeRoomForVirtualRoom(roomId: String): String? {
|
fun nativeRoomForVirtualRoom(roomId: String): String? {
|
||||||
|
if (!protocolsChecker.supportVirtualRooms) return null
|
||||||
val virtualRoom = session.getRoom(roomId) ?: return null
|
val virtualRoom = session.getRoom(roomId) ?: return null
|
||||||
val virtualRoomEvent = virtualRoom.getAccountDataEvent(RoomAccountDataTypes.EVENT_TYPE_VIRTUAL_ROOM)
|
val virtualRoomEvent = virtualRoom.getAccountDataEvent(RoomAccountDataTypes.EVENT_TYPE_VIRTUAL_ROOM)
|
||||||
return virtualRoomEvent?.content?.toModel<RoomVirtualContent>()?.nativeRoomId
|
return virtualRoomEvent?.content?.toModel<RoomVirtualContent>()?.nativeRoomId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun virtualRoomForNativeRoom(roomId: String): String? {
|
||||||
|
if (!protocolsChecker.supportVirtualRooms) return null
|
||||||
|
val virtualRoomEvents = session.accountDataService().getRoomAccountDataEvents(setOf(RoomAccountDataTypes.EVENT_TYPE_VIRTUAL_ROOM))
|
||||||
|
return virtualRoomEvents.firstOrNull {
|
||||||
|
val virtualRoomContent = it.content.toModel<RoomVirtualContent>()
|
||||||
|
virtualRoomContent?.nativeRoomId == roomId
|
||||||
|
}?.roomId
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun getOrCreateVirtualRoomForRoom(roomId: String, opponentUserId: String): String? {
|
suspend fun getOrCreateVirtualRoomForRoom(roomId: String, opponentUserId: String): String? {
|
||||||
protocolsChecker.awaitCheckProtocols()
|
protocolsChecker.awaitCheckProtocols()
|
||||||
if (!protocolsChecker.supportVirtualRooms) return null
|
if (!protocolsChecker.supportVirtualRooms) return null
|
||||||
|
|
|
@ -1614,8 +1614,7 @@ class RoomDetailFragment @Inject constructor(
|
||||||
|
|
||||||
override fun onEventLongClicked(informationData: MessageInformationData, messageContent: Any?, view: View): Boolean {
|
override fun onEventLongClicked(informationData: MessageInformationData, messageContent: Any?, view: View): Boolean {
|
||||||
view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
|
view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
|
||||||
val roomId = roomDetailArgs.roomId
|
val roomId = roomDetailViewModel.timeline.getTimelineEventWithId(informationData.eventId)?.roomId ?: return false
|
||||||
|
|
||||||
this.view?.hideKeyboard()
|
this.view?.hideKeyboard()
|
||||||
|
|
||||||
MessageActionsBottomSheet
|
MessageActionsBottomSheet
|
||||||
|
|
|
@ -48,8 +48,8 @@ import im.vector.app.features.crypto.keysrequest.OutboundSessionKeySharingStrate
|
||||||
import im.vector.app.features.crypto.verification.SupportedVerificationMethodsProvider
|
import im.vector.app.features.crypto.verification.SupportedVerificationMethodsProvider
|
||||||
import im.vector.app.features.home.room.detail.composer.rainbow.RainbowGenerator
|
import im.vector.app.features.home.room.detail.composer.rainbow.RainbowGenerator
|
||||||
import im.vector.app.features.home.room.detail.sticker.StickerPickerActionHandler
|
import im.vector.app.features.home.room.detail.sticker.StickerPickerActionHandler
|
||||||
|
import im.vector.app.features.home.room.detail.timeline.factory.TimelineFactory
|
||||||
import im.vector.app.features.home.room.detail.timeline.helper.RoomSummariesHolder
|
import im.vector.app.features.home.room.detail.timeline.helper.RoomSummariesHolder
|
||||||
import im.vector.app.features.home.room.detail.timeline.helper.TimelineSettingsFactory
|
|
||||||
import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever
|
import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever
|
||||||
import im.vector.app.features.home.room.typing.TypingHelper
|
import im.vector.app.features.home.room.typing.TypingHelper
|
||||||
import im.vector.app.features.powerlevel.PowerLevelsObservableFactory
|
import im.vector.app.features.powerlevel.PowerLevelsObservableFactory
|
||||||
|
@ -118,7 +118,7 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
private val chatEffectManager: ChatEffectManager,
|
private val chatEffectManager: ChatEffectManager,
|
||||||
private val directRoomHelper: DirectRoomHelper,
|
private val directRoomHelper: DirectRoomHelper,
|
||||||
private val jitsiService: JitsiService,
|
private val jitsiService: JitsiService,
|
||||||
timelineSettingsFactory: TimelineSettingsFactory
|
timelineFactory: TimelineFactory
|
||||||
) : VectorViewModel<RoomDetailViewState, RoomDetailAction, RoomDetailViewEvents>(initialState),
|
) : VectorViewModel<RoomDetailViewState, RoomDetailAction, RoomDetailViewEvents>(initialState),
|
||||||
Timeline.Listener, ChatEffectManager.Delegate, CallProtocolsChecker.Listener {
|
Timeline.Listener, ChatEffectManager.Delegate, CallProtocolsChecker.Listener {
|
||||||
|
|
||||||
|
@ -126,9 +126,8 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
private val eventId = initialState.eventId
|
private val eventId = initialState.eventId
|
||||||
private val invisibleEventsObservable = BehaviorRelay.create<RoomDetailAction.TimelineEventTurnsInvisible>()
|
private val invisibleEventsObservable = BehaviorRelay.create<RoomDetailAction.TimelineEventTurnsInvisible>()
|
||||||
private val visibleEventsObservable = BehaviorRelay.create<RoomDetailAction.TimelineEventTurnsVisible>()
|
private val visibleEventsObservable = BehaviorRelay.create<RoomDetailAction.TimelineEventTurnsVisible>()
|
||||||
private val timelineSettings = timelineSettingsFactory.create()
|
|
||||||
private var timelineEvents = PublishRelay.create<List<TimelineEvent>>()
|
private var timelineEvents = PublishRelay.create<List<TimelineEvent>>()
|
||||||
val timeline = room.createTimeline(eventId, timelineSettings)
|
val timeline = timelineFactory.createTimeline(viewModelScope, room, eventId)
|
||||||
|
|
||||||
// Same lifecycle than the ViewModel (survive to screen rotation)
|
// Same lifecycle than the ViewModel (survive to screen rotation)
|
||||||
val previewUrlRetriever = PreviewUrlRetriever(session, viewModelScope)
|
val previewUrlRetriever = PreviewUrlRetriever(session, viewModelScope)
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
package im.vector.app.features.home.room.detail.timeline.factory
|
package im.vector.app.features.home.room.detail.timeline.factory
|
||||||
|
|
||||||
import im.vector.app.core.epoxy.VectorEpoxyModel
|
import im.vector.app.core.epoxy.VectorEpoxyModel
|
||||||
|
import im.vector.app.features.call.vectorCallService
|
||||||
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
||||||
import im.vector.app.features.home.room.detail.timeline.MessageColorProvider
|
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.TimelineEventController
|
||||||
|
@ -26,6 +27,7 @@ import im.vector.app.features.home.room.detail.timeline.helper.RoomSummariesHold
|
||||||
import im.vector.app.features.home.room.detail.timeline.item.CallTileTimelineItem
|
import im.vector.app.features.home.room.detail.timeline.item.CallTileTimelineItem
|
||||||
import im.vector.app.features.home.room.detail.timeline.item.CallTileTimelineItem_
|
import im.vector.app.features.home.room.detail.timeline.item.CallTileTimelineItem_
|
||||||
import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData
|
import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData
|
||||||
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
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.events.model.toModel
|
||||||
import org.matrix.android.sdk.api.session.room.model.call.CallAnswerContent
|
import org.matrix.android.sdk.api.session.room.model.call.CallAnswerContent
|
||||||
|
@ -38,6 +40,7 @@ import org.matrix.android.sdk.api.util.toMatrixItem
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class CallItemFactory @Inject constructor(
|
class CallItemFactory @Inject constructor(
|
||||||
|
private val session: Session,
|
||||||
private val messageColorProvider: MessageColorProvider,
|
private val messageColorProvider: MessageColorProvider,
|
||||||
private val messageInformationDataFactory: MessageInformationDataFactory,
|
private val messageInformationDataFactory: MessageInformationDataFactory,
|
||||||
private val messageItemAttributesFactory: MessageItemAttributesFactory,
|
private val messageItemAttributesFactory: MessageItemAttributesFactory,
|
||||||
|
@ -132,7 +135,8 @@ class CallItemFactory @Inject constructor(
|
||||||
isStillActive: Boolean,
|
isStillActive: Boolean,
|
||||||
callback: TimelineEventController.Callback?
|
callback: TimelineEventController.Callback?
|
||||||
): CallTileTimelineItem? {
|
): CallTileTimelineItem? {
|
||||||
val userOfInterest = roomSummariesHolder.get(roomId)?.toMatrixItem() ?: return null
|
val correctedRoomId = session.vectorCallService.userMapper.nativeRoomForVirtualRoom(roomId) ?: roomId
|
||||||
|
val userOfInterest = roomSummariesHolder.get(correctedRoomId)?.toMatrixItem() ?: return null
|
||||||
val attributes = messageItemAttributesFactory.create(null, informationData, callback).let {
|
val attributes = messageItemAttributesFactory.create(null, informationData, callback).let {
|
||||||
CallTileTimelineItem.Attributes(
|
CallTileTimelineItem.Attributes(
|
||||||
callId = callId,
|
callId = callId,
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* 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.call.vectorCallService
|
||||||
|
import im.vector.app.features.home.room.detail.timeline.helper.TimelineSettingsFactory
|
||||||
|
import im.vector.app.features.home.room.detail.timeline.merged.MergedTimelines
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import org.matrix.android.sdk.api.session.Session
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||||
|
import org.matrix.android.sdk.api.session.room.Room
|
||||||
|
import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
private val secondaryTimelineAllowedTypes = listOf(
|
||||||
|
EventType.CALL_HANGUP,
|
||||||
|
EventType.CALL_INVITE,
|
||||||
|
EventType.CALL_REJECT,
|
||||||
|
EventType.CALL_ANSWER
|
||||||
|
)
|
||||||
|
|
||||||
|
class TimelineFactory @Inject constructor(private val session: Session, private val timelineSettingsFactory: TimelineSettingsFactory) {
|
||||||
|
|
||||||
|
fun createTimeline(coroutineScope: CoroutineScope, mainRoom: Room, eventId: String?): Timeline {
|
||||||
|
val settings = timelineSettingsFactory.create()
|
||||||
|
if (!session.vectorCallService.protocolChecker.supportVirtualRooms) {
|
||||||
|
return mainRoom.createTimeline(eventId, settings)
|
||||||
|
}
|
||||||
|
val virtualRoomId = session.vectorCallService.userMapper.virtualRoomForNativeRoom(mainRoom.roomId)
|
||||||
|
return if (virtualRoomId == null) {
|
||||||
|
mainRoom.createTimeline(eventId, settings)
|
||||||
|
} else {
|
||||||
|
val virtualRoom = session.getRoom(virtualRoomId)!!
|
||||||
|
MergedTimelines(
|
||||||
|
coroutineScope = coroutineScope,
|
||||||
|
mainTimeline = mainRoom.createTimeline(eventId, settings),
|
||||||
|
secondaryTimelineParams = MergedTimelines.SecondaryTimelineParams(
|
||||||
|
timeline = virtualRoom.createTimeline(null, settings),
|
||||||
|
shouldFilterTypes = true,
|
||||||
|
allowedTypes = secondaryTimelineAllowedTypes
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,223 @@
|
||||||
|
/*
|
||||||
|
* 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.merged
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.sync.Semaphore
|
||||||
|
import kotlinx.coroutines.sync.withPermit
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||||
|
import org.matrix.android.sdk.api.session.room.sender.SenderInfo
|
||||||
|
import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
||||||
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||||
|
import kotlin.reflect.KMutableProperty0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This can be use to merge timeline tiles from 2 different rooms.
|
||||||
|
* Be aware it wont work properly with permalink.
|
||||||
|
*/
|
||||||
|
class MergedTimelines(
|
||||||
|
private val coroutineScope: CoroutineScope,
|
||||||
|
private val mainTimeline: Timeline,
|
||||||
|
private val secondaryTimelineParams: SecondaryTimelineParams) : Timeline by mainTimeline {
|
||||||
|
|
||||||
|
data class SecondaryTimelineParams(
|
||||||
|
val timeline: Timeline,
|
||||||
|
val disableReadReceipts: Boolean = true,
|
||||||
|
val shouldFilterTypes: Boolean = false,
|
||||||
|
val allowedTypes: List<String> = emptyList()
|
||||||
|
)
|
||||||
|
|
||||||
|
private var mainIsInit = false
|
||||||
|
private var secondaryIsInit = false
|
||||||
|
private val secondaryTimeline = secondaryTimelineParams.timeline
|
||||||
|
|
||||||
|
private val listenersMapping = HashMap<Timeline.Listener, List<ListenerInterceptor>>()
|
||||||
|
private val mainTimelineEvents = ArrayList<TimelineEvent>()
|
||||||
|
private val secondaryTimelineEvents = ArrayList<TimelineEvent>()
|
||||||
|
private val positionsMapping = HashMap<String, Int>()
|
||||||
|
private val mergedEvents = ArrayList<TimelineEvent>()
|
||||||
|
|
||||||
|
private val processingSemaphore = Semaphore(1)
|
||||||
|
|
||||||
|
private class ListenerInterceptor(
|
||||||
|
var timeline: Timeline?,
|
||||||
|
private val wrappedListener: Timeline.Listener,
|
||||||
|
private val shouldFilterTypes: Boolean,
|
||||||
|
private val allowedTypes: List<String>,
|
||||||
|
private val onTimelineUpdate: (List<TimelineEvent>) -> Unit
|
||||||
|
) : Timeline.Listener by wrappedListener {
|
||||||
|
|
||||||
|
override fun onTimelineUpdated(snapshot: List<TimelineEvent>) {
|
||||||
|
val filteredEvents = if (shouldFilterTypes) {
|
||||||
|
snapshot.filter {
|
||||||
|
allowedTypes.contains(it.root.getClearType())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
snapshot
|
||||||
|
}
|
||||||
|
onTimelineUpdate(filteredEvents)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addListener(listener: Timeline.Listener): Boolean {
|
||||||
|
val mainTimelineListener = ListenerInterceptor(
|
||||||
|
timeline = mainTimeline,
|
||||||
|
wrappedListener = listener,
|
||||||
|
shouldFilterTypes = false,
|
||||||
|
allowedTypes = emptyList()) {
|
||||||
|
processTimelineUpdates(::mainIsInit, mainTimelineEvents, it)
|
||||||
|
}
|
||||||
|
val secondaryTimelineListener = ListenerInterceptor(
|
||||||
|
timeline = secondaryTimeline,
|
||||||
|
wrappedListener = listener,
|
||||||
|
shouldFilterTypes = secondaryTimelineParams.shouldFilterTypes,
|
||||||
|
allowedTypes = secondaryTimelineParams.allowedTypes) {
|
||||||
|
processTimelineUpdates(::secondaryIsInit, secondaryTimelineEvents, it)
|
||||||
|
}
|
||||||
|
listenersMapping[listener] = listOf(mainTimelineListener, secondaryTimelineListener)
|
||||||
|
return mainTimeline.addListener(mainTimelineListener) && secondaryTimeline.addListener(secondaryTimelineListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeListener(listener: Timeline.Listener): Boolean {
|
||||||
|
return listenersMapping.remove(listener)?.let {
|
||||||
|
it.forEach { listener ->
|
||||||
|
listener.timeline?.removeListener(listener)
|
||||||
|
listener.timeline = null
|
||||||
|
}
|
||||||
|
true
|
||||||
|
} ?: false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeAllListeners() {
|
||||||
|
mainTimeline.removeAllListeners()
|
||||||
|
secondaryTimeline.removeAllListeners()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun start() {
|
||||||
|
mainTimeline.start()
|
||||||
|
secondaryTimeline.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun dispose() {
|
||||||
|
mainTimeline.dispose()
|
||||||
|
secondaryTimeline.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun restartWithEventId(eventId: String?) {
|
||||||
|
mainTimeline.restartWithEventId(eventId)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hasMoreToLoad(direction: Timeline.Direction): Boolean {
|
||||||
|
return mainTimeline.hasMoreToLoad(direction) || secondaryTimeline.hasMoreToLoad(direction)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun paginate(direction: Timeline.Direction, count: Int) {
|
||||||
|
mainTimeline.paginate(direction, count)
|
||||||
|
secondaryTimeline.paginate(direction, count)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun pendingEventCount(): Int {
|
||||||
|
return mainTimeline.pendingEventCount() + secondaryTimeline.pendingEventCount()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun failedToDeliverEventCount(): Int {
|
||||||
|
return mainTimeline.pendingEventCount() + secondaryTimeline.pendingEventCount()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getTimelineEventAtIndex(index: Int): TimelineEvent? {
|
||||||
|
return mergedEvents.getOrNull(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getIndexOfEvent(eventId: String?): Int? {
|
||||||
|
return positionsMapping[eventId]
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getTimelineEventWithId(eventId: String?): TimelineEvent? {
|
||||||
|
return positionsMapping[eventId]?.let {
|
||||||
|
getTimelineEventAtIndex(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processTimelineUpdates(isInit: KMutableProperty0<Boolean>, eventsRef: MutableList<TimelineEvent>, newData: List<TimelineEvent>) {
|
||||||
|
coroutineScope.launch(Dispatchers.Default) {
|
||||||
|
processingSemaphore.withPermit {
|
||||||
|
isInit.set(true)
|
||||||
|
eventsRef.apply {
|
||||||
|
clear()
|
||||||
|
addAll(newData)
|
||||||
|
}
|
||||||
|
mergeTimeline()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun mergeTimeline() {
|
||||||
|
val merged = mutableListOf<TimelineEvent>()
|
||||||
|
val mainItr = mainTimelineEvents.toList().listIterator()
|
||||||
|
val secondaryItr = secondaryTimelineEvents.toList().listIterator()
|
||||||
|
var index = 0
|
||||||
|
var correctedSenderInfo: SenderInfo? = mainTimelineEvents.firstOrNull()?.senderInfo
|
||||||
|
if (!mainIsInit || !secondaryIsInit) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
while (merged.size < mainTimelineEvents.size + secondaryTimelineEvents.size) {
|
||||||
|
if (mainItr.hasNext()) {
|
||||||
|
val nextMain = mainItr.next()
|
||||||
|
correctedSenderInfo = nextMain.senderInfo
|
||||||
|
if (secondaryItr.hasNext()) {
|
||||||
|
val nextSecondary = secondaryItr.next()
|
||||||
|
if (nextSecondary.root.originServerTs ?: 0 > nextMain.root.originServerTs ?: 0) {
|
||||||
|
positionsMapping[nextSecondary.eventId] = index
|
||||||
|
merged.add(nextSecondary.correctBeforeMerging(correctedSenderInfo))
|
||||||
|
mainItr.previous()
|
||||||
|
} else {
|
||||||
|
positionsMapping[nextMain.eventId] = index
|
||||||
|
merged.add(nextMain)
|
||||||
|
secondaryItr.previous()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
positionsMapping[nextMain.eventId] = index
|
||||||
|
merged.add(nextMain)
|
||||||
|
}
|
||||||
|
} else if (secondaryItr.hasNext()) {
|
||||||
|
val nextSecondary = secondaryItr.next()
|
||||||
|
positionsMapping[nextSecondary.eventId] = index
|
||||||
|
merged.add(nextSecondary.correctBeforeMerging(correctedSenderInfo))
|
||||||
|
}
|
||||||
|
index++
|
||||||
|
}
|
||||||
|
mergedEvents.apply {
|
||||||
|
clear()
|
||||||
|
addAll(merged)
|
||||||
|
}
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
listenersMapping.keys.forEach { listener ->
|
||||||
|
tryOrNull { listener.onTimelineUpdated(merged) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun TimelineEvent.correctBeforeMerging(correctedSenderInfo: SenderInfo?): TimelineEvent {
|
||||||
|
return copy(
|
||||||
|
senderInfo = correctedSenderInfo ?: senderInfo,
|
||||||
|
readReceipts = if (secondaryTimelineParams.disableReadReceipts) emptyList() else readReceipts
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -53,7 +53,7 @@
|
||||||
android:layout_marginTop="12dp"
|
android:layout_marginTop="12dp"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:layout_marginBottom="12dp"
|
android:layout_marginBottom="12dp"
|
||||||
android:textColor="?vctr_notice_secondary"
|
android:textColor="?vctr_content_secondary"
|
||||||
tools:text="@string/video_call_in_progress" />
|
tools:text="@string/video_call_in_progress" />
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue