diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt index 778550a7c4..9693e56ff0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt @@ -38,7 +38,6 @@ import org.matrix.android.sdk.internal.database.model.TimelineEventEntity import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory -import org.matrix.android.sdk.internal.session.room.send.LocalEchoRepository import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor import org.matrix.android.sdk.internal.task.TaskExecutor import org.matrix.android.sdk.internal.task.configureWith diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt index 9d003c1ff6..d000bbeb50 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt @@ -87,7 +87,7 @@ internal class DefaultTimelineService @AssistedInject constructor(@Assisted priv } override fun getTimeLineEventLive(eventId: String): LiveData<Optional<TimelineEvent>> { - return LiveTimelineEvent(timelineInput, monarchy, taskExecutor, timelineEventMapper, roomId, eventId) + return LiveTimelineEvent(timelineInput, monarchy, taskExecutor.executorScope, timelineEventMapper, roomId, eventId) } override fun getAttachmentMessages(): List<TimelineEvent> { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LiveTimelineEvent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LiveTimelineEvent.kt index 0e9c917b05..3c0f101e11 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LiveTimelineEvent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LiveTimelineEvent.kt @@ -20,6 +20,7 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MediatorLiveData import androidx.lifecycle.Transformations import com.zhuinden.monarchy.Monarchy +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.events.model.LocalEcho @@ -29,14 +30,14 @@ import org.matrix.android.sdk.api.util.toOptional import org.matrix.android.sdk.internal.database.mapper.TimelineEventMapper import org.matrix.android.sdk.internal.database.model.TimelineEventEntity import org.matrix.android.sdk.internal.database.query.where -import org.matrix.android.sdk.internal.task.TaskExecutor +import java.util.concurrent.atomic.AtomicBoolean /** * This class takes care of handling case where local echo is replaced by the synced event in the db. */ internal class LiveTimelineEvent(private val timelineInput: TimelineInput, private val monarchy: Monarchy, - private val taskExecutor: TaskExecutor, + private val coroutineScope: CoroutineScope, private val timelineEventMapper: TimelineEventMapper, private val roomId: String, private val eventId: String) @@ -45,12 +46,15 @@ internal class LiveTimelineEvent(private val timelineInput: TimelineInput, private var queryLiveData: LiveData<Optional<TimelineEvent>>? = null + // If we are listening to local echo, we want to be aware when event is synced + private var shouldObserveSync = AtomicBoolean(LocalEcho.isLocalEchoId(eventId)) + init { buildAndObserveQuery(eventId) } // Makes sure it's made on the main thread - private fun buildAndObserveQuery(eventIdToObserve: String) = taskExecutor.executorScope.launch(Dispatchers.Main) { + private fun buildAndObserveQuery(eventIdToObserve: String) = coroutineScope.launch(Dispatchers.Main) { queryLiveData?.also { removeSource(it) } @@ -60,14 +64,15 @@ internal class LiveTimelineEvent(private val timelineInput: TimelineInput, ) queryLiveData = Transformations.map(liveData) { events -> events.firstOrNull().toOptional() - } - queryLiveData?.also { + }.also { addSource(it) { newValue -> value = newValue } } } override fun onLocalEchoSynced(roomId: String, localEchoEventId: String, syncedEventId: String) { - if (localEchoEventId == eventId) { + if (this.roomId == roomId && localEchoEventId == this.eventId) { + timelineInput.listeners.remove(this) + shouldObserveSync.set(false) // rebuild the query with the new eventId buildAndObserveQuery(syncedEventId) } @@ -75,15 +80,14 @@ internal class LiveTimelineEvent(private val timelineInput: TimelineInput, override fun onActive() { super.onActive() - // If we are listening to local echo, we want to be aware when event is synced - if (LocalEcho.isLocalEchoId(eventId)) { + if (shouldObserveSync.get()) { timelineInput.listeners.add(this) } } override fun onInactive() { super.onInactive() - if (LocalEcho.isLocalEchoId(eventId)) { + if (shouldObserveSync.get()) { timelineInput.listeners.remove(this) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt index 0b1f67c7b7..adf315a955 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt @@ -18,6 +18,7 @@ package im.vector.app.features.home.room.detail.timeline.action import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext +import com.jakewharton.rxrelay2.BehaviorRelay import dagger.Lazy import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -75,6 +76,8 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted pillsPostProcessorFactory.create(initialState.roomId) } + private val eventIdObservable = BehaviorRelay.createDefault(initialState.eventId) + @AssistedFactory interface Factory { fun create(initialState: MessageActionState): MessageActionsViewModel @@ -90,6 +93,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted init { observeEvent() + observeReactions() observePowerLevel() observeTimelineEventState() } @@ -135,14 +139,17 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted } } - private fun observeReactions(eventId: String) { + private fun observeReactions() { if (room == null) return - room.rx() - .liveAnnotationSummary(eventId) - .map { annotations -> - EmojiDataSource.quickEmojis.map { emoji -> - ToggleState(emoji, annotations.getOrNull()?.reactionsSummary?.firstOrNull { it.key == emoji }?.addedByMe ?: false) - } + eventIdObservable + .switchMap { eventId -> + room.rx() + .liveAnnotationSummary(eventId) + .map { annotations -> + EmojiDataSource.quickEmojis.map { emoji -> + ToggleState(emoji, annotations.getOrNull()?.reactionsSummary?.firstOrNull { it.key == emoji }?.addedByMe ?: false) + } + } } .execute { copy(quickStates = it) @@ -152,7 +159,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted private fun observeTimelineEventState() { selectSubscribe(MessageActionState::timelineEvent, MessageActionState::actionPermissions) { timelineEvent, permissions -> val nonNullTimelineEvent = timelineEvent() ?: return@selectSubscribe - observeReactions(nonNullTimelineEvent.eventId) + eventIdObservable.accept(nonNullTimelineEvent.eventId) setState { copy( eventId = nonNullTimelineEvent.eventId,