From a734c699adb03c1793a066bb3ad3c2973be71aca Mon Sep 17 00:00:00 2001 From: Valere Date: Fri, 28 Jun 2019 19:28:38 +0200 Subject: [PATCH] Fix impure reducer and use live event --- .../main/java/im/vector/matrix/rx/RxRoom.kt | 7 ++- .../room/model/relation/RelationService.kt | 2 +- .../session/room/timeline/TimelineService.kt | 5 ++ .../room/relation/DefaultRelationService.kt | 18 +++--- .../room/timeline/DefaultTimelineService.kt | 26 +++++++++ .../action/MessageActionsViewModel.kt | 57 ++++++++++--------- .../timeline/action/MessageMenuFragment.kt | 5 +- .../timeline/action/MessageMenuViewModel.kt | 54 +++++++++++------- .../timeline/action/QuickReactionFragment.kt | 5 +- .../timeline/action/QuickReactionViewModel.kt | 39 ++++++++----- .../timeline/action/ViewReactionViewModel.kt | 9 +-- 11 files changed, 145 insertions(+), 82 deletions(-) diff --git a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt index 66ddc550..8a137518 100644 --- a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt +++ b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt @@ -19,6 +19,7 @@ package im.vector.matrix.rx import im.vector.matrix.android.api.session.room.Room import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary import im.vector.matrix.android.api.session.room.model.RoomSummary +import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import io.reactivex.Observable class RxRoom(private val room: Room) { @@ -31,10 +32,14 @@ class RxRoom(private val room: Room) { return room.getRoomMemberIdsLive().asObservable() } - fun liveAnnotationSummary(eventId: String): Observable> { + fun liveAnnotationSummary(eventId: String): Observable { return room.getEventSummaryLive(eventId).asObservable() } + fun liveTimelineEvent(eventId: String): Observable { + return room.liveTimeLineEvent(eventId).asObservable() + } + } fun Room.rx(): RxRoom { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/relation/RelationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/relation/RelationService.kt index bef89839..2a3c5b6d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/relation/RelationService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/relation/RelationService.kt @@ -80,5 +80,5 @@ interface RelationService { */ fun replyToMessage(eventReplied: Event, replyText: String): Cancelable? - fun getEventSummaryLive(eventId: String): LiveData> + fun getEventSummaryLive(eventId: String): LiveData } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineService.kt index 295ddd07..5a4838ad 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineService.kt @@ -16,6 +16,8 @@ package im.vector.matrix.android.api.session.room.timeline +import androidx.lifecycle.LiveData + /** * This interface defines methods to interact with the timeline. It's implemented at the room level. */ @@ -32,4 +34,7 @@ interface TimelineService { fun getTimeLineEvent(eventId: String): TimelineEvent? + + + fun liveTimeLineEvent(eventId: String): LiveData } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/DefaultRelationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/DefaultRelationService.kt index cf0b3f1c..42f7f0ca 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/DefaultRelationService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/DefaultRelationService.kt @@ -17,6 +17,7 @@ package im.vector.matrix.android.internal.session.room.relation import android.content.Context import androidx.lifecycle.LiveData +import androidx.lifecycle.Transformations import androidx.work.OneTimeWorkRequest import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.MatrixCallback @@ -27,6 +28,7 @@ import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary import im.vector.matrix.android.api.session.room.model.message.MessageType import im.vector.matrix.android.api.session.room.model.relation.RelationService import im.vector.matrix.android.api.util.Cancelable +import im.vector.matrix.android.internal.database.RealmLiveData import im.vector.matrix.android.internal.database.helper.addSendingEvent import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.model.ChunkEntity @@ -155,15 +157,13 @@ internal class DefaultRelationService @Inject constructor(private val context: C return workRequest } - override fun getEventSummaryLive(eventId: String): LiveData> { - return monarchy.findAllMappedWithChanges( - { realm -> - EventAnnotationsSummaryEntity.where(realm, eventId) - }, - { - it.asDomain() - } - ) + override fun getEventSummaryLive(eventId: String): LiveData { + val liveEntity = RealmLiveData(monarchy.realmConfiguration) { realm -> + EventAnnotationsSummaryEntity.where(realm, eventId) + } + return Transformations.map(liveEntity) { realmResults -> + realmResults.firstOrNull()?.asDomain() ?: EventAnnotationsSummary(eventId, emptyList(), null) + } } /** diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineService.kt index fbf22c37..dbd40ad1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineService.kt @@ -16,10 +16,14 @@ package im.vector.matrix.android.internal.session.room.timeline +import androidx.lifecycle.LiveData +import androidx.lifecycle.MediatorLiveData import com.zhuinden.monarchy.Monarchy 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.TimelineService +import im.vector.matrix.android.internal.database.RealmLiveData +import im.vector.matrix.android.internal.database.model.EventAnnotationsSummaryEntity import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.task.TaskExecutor @@ -47,5 +51,27 @@ internal class DefaultTimelineService @Inject constructor(private val roomId: St }) } + override fun liveTimeLineEvent(eventId: String): LiveData { + val liveEventEntity = RealmLiveData(monarchy.realmConfiguration) { realm -> + EventEntity.where(realm, eventId = eventId) + } + val liveAnnotationsEntity = RealmLiveData(monarchy.realmConfiguration) { realm -> + EventAnnotationsSummaryEntity.where(realm, eventId = eventId) + } + val result = MediatorLiveData() + result.addSource(liveEventEntity) { realmResults -> + result.value = realmResults.firstOrNull()?.let { timelineEventFactory.create(it) } + } + + result.addSource(liveAnnotationsEntity) { + liveEventEntity.value?.let { + result.value = liveEventEntity.value?.let { realmResults -> + //recreate the timeline event + realmResults.firstOrNull()?.let { timelineEventFactory.create(it) } + } + } + } + return result + } } \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageActionsViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageActionsViewModel.kt index 37e38a5f..58c8cf61 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageActionsViewModel.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageActionsViewModel.kt @@ -15,10 +15,7 @@ */ package im.vector.riotredesign.features.home.room.detail.timeline.action -import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxState -import com.airbnb.mvrx.MvRxViewModelFactory -import com.airbnb.mvrx.ViewModelContext +import com.airbnb.mvrx.* import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject import im.vector.matrix.android.api.session.Session @@ -28,7 +25,7 @@ import im.vector.matrix.android.api.session.room.model.message.MessageContent import im.vector.matrix.android.api.session.room.model.message.MessageTextContent import im.vector.matrix.android.api.session.room.model.message.MessageType import im.vector.matrix.android.api.session.room.timeline.TimelineEvent -import im.vector.riotredesign.core.di.HasScreenInjector +import im.vector.matrix.rx.RxRoom import im.vector.riotredesign.core.platform.VectorViewModel import im.vector.riotredesign.features.home.room.detail.timeline.format.NoticeEventFormatter import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData @@ -37,27 +34,30 @@ import java.text.SimpleDateFormat import java.util.* -val dateFormat = SimpleDateFormat("EEE, d MMM yyyy HH:mm", Locale.getDefault()) - data class MessageActionState( val roomId: String, val eventId: String, val informationData: MessageInformationData, - val timelineEvent: TimelineEvent? + val timelineEvent: Async = Uninitialized ) : MvRxState { + constructor(args: TimelineEventFragmentArgs) : this(roomId = args.roomId, eventId = args.eventId, informationData = args.informationData) + + + private val dateFormat = SimpleDateFormat("EEE, d MMM yyyy HH:mm", Locale.getDefault()) + fun senderName(): String = informationData.memberName?.toString() ?: "" - fun time(): String? = timelineEvent?.root?.originServerTs?.let { dateFormat.format(Date(it)) } + fun time(): String? = timelineEvent()?.root?.originServerTs?.let { dateFormat.format(Date(it)) } ?: "" - fun canReact(): Boolean = timelineEvent?.root?.type == EventType.MESSAGE && timelineEvent.sendState.isSent() + fun canReact(): Boolean = timelineEvent()?.root?.type == EventType.MESSAGE && timelineEvent()?.sendState?.isSent() == true fun messageBody(eventHtmlRenderer: EventHtmlRenderer?, noticeEventFormatter: NoticeEventFormatter?): CharSequence? { - return when (timelineEvent?.root?.getClearType()) { + return when (timelineEvent()?.root?.getClearType()) { EventType.MESSAGE -> { - val messageContent: MessageContent? = timelineEvent.annotations?.editSummary?.aggregatedContent?.toModel() - ?: timelineEvent.root.getClearContent().toModel() + val messageContent: MessageContent? = timelineEvent()?.annotations?.editSummary?.aggregatedContent?.toModel() + ?: timelineEvent()?.root?.getClearContent().toModel() if (messageContent is MessageTextContent && messageContent.format == MessageType.FORMAT_MATRIX_HTML) { eventHtmlRenderer?.render(messageContent.formattedBody ?: messageContent.body) @@ -72,7 +72,7 @@ data class MessageActionState( EventType.CALL_INVITE, EventType.CALL_HANGUP, EventType.CALL_ANSWER -> { - noticeEventFormatter?.format(timelineEvent) + timelineEvent()?.let { noticeEventFormatter?.format(it) } } else -> null } @@ -85,10 +85,14 @@ data class MessageActionState( class MessageActionsViewModel @AssistedInject constructor(@Assisted initialState: MessageActionState, private val eventHtmlRenderer: EventHtmlRenderer, - private val session: Session, + session: Session, private val noticeEventFormatter: NoticeEventFormatter ) : VectorViewModel(initialState) { + + private val eventId = initialState.eventId + private val room = session.getRoom(initialState.roomId) + @AssistedInject.Factory interface Factory { fun create(initialState: MessageActionState): MessageActionsViewModel @@ -101,18 +105,19 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted return fragment.messageActionViewModelFactory.create(state) } - override fun initialState(viewModelContext: ViewModelContext): MessageActionState? { - val session = (viewModelContext.activity as HasScreenInjector).injector().session() - val args: TimelineEventFragmentArgs = viewModelContext.args() - val event = session.getRoom(args.roomId)?.getTimeLineEvent(args.eventId) - return MessageActionState( - args.roomId, - args.eventId, - args.informationData, - event - ) - } + } + init { + observeEvent() + } + + private fun observeEvent() { + if (room == null) return + RxRoom(room) + .liveTimelineEvent(eventId) + .execute { + copy(timelineEvent = it) + } } fun resolveBody(state: MessageActionState): CharSequence? { diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageMenuFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageMenuFragment.kt index a45df7e1..4c8bc2c5 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageMenuFragment.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageMenuFragment.kt @@ -55,7 +55,8 @@ class MessageMenuFragment : VectorBaseFragment() { val inflater = LayoutInflater.from(linearLayout.context) linearLayout.removeAllViews() var insertIndex = 0 - state.actions.forEachIndexed { index, action -> + val actions = state.actions() + actions?.forEachIndexed { index, action -> inflateActionView(action, inflater, linearLayout)?.let { it.setOnClickListener { interactionListener?.didSelectMenuAction(action) @@ -63,7 +64,7 @@ class MessageMenuFragment : VectorBaseFragment() { linearLayout.addView(it, insertIndex) insertIndex++ if (addSeparators) { - if (index < state.actions.size - 1) { + if (index < actions.size - 1) { linearLayout.addView(inflateSeparatorView(), insertIndex) insertIndex++ } diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageMenuViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageMenuViewModel.kt index c0f8baa9..fcbe01ab 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageMenuViewModel.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageMenuViewModel.kt @@ -15,10 +15,7 @@ */ package im.vector.riotredesign.features.home.room.detail.timeline.action -import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxState -import com.airbnb.mvrx.MvRxViewModelFactory -import com.airbnb.mvrx.ViewModelContext +import com.airbnb.mvrx.* import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject import im.vector.matrix.android.api.session.Session @@ -30,6 +27,7 @@ import im.vector.matrix.android.api.session.room.model.message.MessageImageConte import im.vector.matrix.android.api.session.room.model.message.MessageType import im.vector.matrix.android.api.session.room.send.SendState import im.vector.matrix.android.api.session.room.timeline.TimelineEvent +import im.vector.matrix.rx.RxRoom import im.vector.riotredesign.R import im.vector.riotredesign.core.platform.VectorViewModel import im.vector.riotredesign.core.resources.StringProvider @@ -44,7 +42,7 @@ data class MessageMenuState( val roomId: String, val eventId: String, val informationData: MessageInformationData, - val actions: List = emptyList() + val actions: Async> = Uninitialized ) : MvRxState { constructor(args: TimelineEventFragmentArgs) : this(roomId = args.roomId, eventId = args.eventId, informationData = args.informationData) @@ -63,6 +61,12 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M fun create(initialState: MessageMenuState): MessageMenuViewModel } + private val room = session.getRoom(initialState.roomId) + ?: throw IllegalStateException("Shouldn't use this ViewModel without a room") + + private val eventId = initialState.eventId + private val informationData: MessageInformationData = initialState.informationData + companion object : MvRxViewModelFactory { const val ACTION_ADD_REACTION = "add_reaction" @@ -87,13 +91,23 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M } init { - setState { reduceState(this) } + observeEvent() } - private fun reduceState(state: MessageMenuState): MessageMenuState { - val event = session.getRoom(state.roomId)?.getTimeLineEvent(state.eventId) ?: return state + private fun observeEvent() { + RxRoom(room) + .liveTimelineEvent(eventId) + ?.map { + actionsForEvent(it) + } + ?.execute { + copy(actions = it) + } + } - val messageContent: MessageContent? = event.annotations?.editSummary?.aggregatedContent?.toModel() + private fun actionsForEvent(event: TimelineEvent): List { + + val messageContent: MessageContent? = event.annotations?.editSummary?.aggregatedContent.toModel() ?: event.root.getClearContent().toModel() val type = messageContent?.type @@ -114,7 +128,7 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M //TODO is downloading attachement? if (canReact(event, messageContent)) { - this.add(SimpleAction(ACTION_ADD_REACTION, R.string.message_add_reaction, R.drawable.ic_add_reaction, event.root.eventId)) + this.add(SimpleAction(ACTION_ADD_REACTION, R.string.message_add_reaction, R.drawable.ic_add_reaction, eventId)) } if (canCopy(type)) { //TODO copy images? html? see ClipBoard @@ -122,23 +136,23 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M } if (canReply(event, messageContent)) { - this.add(SimpleAction(ACTION_REPLY, R.string.reply, R.drawable.ic_reply, event.root.eventId)) + this.add(SimpleAction(ACTION_REPLY, R.string.reply, R.drawable.ic_reply, eventId)) } if (canEdit(event, session.sessionParams.credentials.userId)) { - this.add(SimpleAction(ACTION_EDIT, R.string.edit, R.drawable.ic_edit, event.root.eventId)) + this.add(SimpleAction(ACTION_EDIT, R.string.edit, R.drawable.ic_edit, eventId)) } if (canRedact(event, session.sessionParams.credentials.userId)) { - this.add(SimpleAction(ACTION_DELETE, R.string.delete, R.drawable.ic_delete, event.root.eventId)) + this.add(SimpleAction(ACTION_DELETE, R.string.delete, R.drawable.ic_delete, eventId)) } if (canQuote(event, messageContent)) { - this.add(SimpleAction(ACTION_QUOTE, R.string.quote, R.drawable.ic_quote, state.eventId)) + this.add(SimpleAction(ACTION_QUOTE, R.string.quote, R.drawable.ic_quote, eventId)) } if (canViewReactions(event)) { - this.add(SimpleAction(ACTION_VIEW_REACTIONS, R.string.message_view_reaction, R.drawable.ic_view_reactions, state.informationData)) + this.add(SimpleAction(ACTION_VIEW_REACTIONS, R.string.message_view_reaction, R.drawable.ic_view_reactions, informationData)) } if (canShare(type)) { @@ -160,22 +174,22 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M //TODO sent by me or sufficient power level } - this.add(SimpleAction(VIEW_SOURCE, R.string.view_source, R.drawable.ic_view_source, JSONObject(event.root.toContent()).toString(4))) + this.add(SimpleAction(VIEW_SOURCE, R.string.view_source, R.drawable.ic_view_source, JSONObject(event.root.content.toContent()).toString(4))) if (event.isEncrypted()) { - val decryptedContent = event.root.mClearEvent?.toContent()?.let { + val decryptedContent = event.root.mClearEvent?.content?.toContent().let { JSONObject(it).toString(4) } ?: stringProvider.getString(R.string.encryption_information_decryption_error) this.add(SimpleAction(VIEW_DECRYPTED_SOURCE, R.string.view_decrypted_source, R.drawable.ic_view_source, decryptedContent)) } - this.add(SimpleAction(ACTION_COPY_PERMALINK, R.string.permalink, R.drawable.ic_permalink, state.eventId)) + this.add(SimpleAction(ACTION_COPY_PERMALINK, R.string.permalink, R.drawable.ic_permalink, event.root.eventId)) if (session.sessionParams.credentials.userId != event.root.senderId && event.root.getClearType() == EventType.MESSAGE) { //not sent by me - this.add(SimpleAction(ACTION_FLAG, R.string.report_content, R.drawable.ic_flag, state.eventId)) + this.add(SimpleAction(ACTION_FLAG, R.string.report_content, R.drawable.ic_flag, event.root.eventId)) } } } - return state.copy(actions = actions) + return actions } diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/QuickReactionFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/QuickReactionFragment.kt index 994247fa..d5d64231 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/QuickReactionFragment.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/QuickReactionFragment.kt @@ -47,7 +47,7 @@ class QuickReactionFragment : VectorBaseFragment() { injector.inject(this) } - lateinit var textViews: List + private lateinit var textViews: List override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -63,7 +63,8 @@ class QuickReactionFragment : VectorBaseFragment() { } override fun invalidate() = withState(viewModel) { - it.quickStates.forEachIndexed { index, qs -> + val quickReactionsStates = it.quickStates() ?: return@withState + quickReactionsStates.forEachIndexed { index, qs -> textViews[index].text = qs.reaction textViews[index].alpha = if (qs.isSelected) 0.2f else 1f } diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/QuickReactionViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/QuickReactionViewModel.kt index 85a22f9a..3d634fe8 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/QuickReactionViewModel.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/QuickReactionViewModel.kt @@ -15,13 +15,11 @@ */ package im.vector.riotredesign.features.home.room.detail.timeline.action -import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxState -import com.airbnb.mvrx.MvRxViewModelFactory -import com.airbnb.mvrx.ViewModelContext +import com.airbnb.mvrx.* import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject import im.vector.matrix.android.api.session.Session +import im.vector.matrix.rx.RxRoom import im.vector.riotredesign.core.platform.VectorViewModel import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData @@ -37,7 +35,7 @@ data class QuickReactionState( val roomId: String, val eventId: String, val informationData: MessageInformationData, - val quickStates: List = emptyList(), + val quickStates: Async> = Uninitialized, val result: ToggleState? = null /** Pair of 'clickedOn' and current toggles state*/ ) : MvRxState { @@ -56,6 +54,9 @@ class QuickReactionViewModel @AssistedInject constructor(@Assisted initialState: fun create(initialState: QuickReactionState): QuickReactionViewModel } + private val room = session.getRoom(initialState.roomId) + private val eventId = initialState.eventId + companion object : MvRxViewModelFactory { val quickEmojis = listOf("👍", "👎", "😄", "🎉", "😕", "❤️", "🚀", "👀") @@ -67,22 +68,30 @@ class QuickReactionViewModel @AssistedInject constructor(@Assisted initialState: } init { - setState { reduceState(this) } + observeReactions() } - private fun reduceState(state: QuickReactionState): QuickReactionState { - val event = session.getRoom(state.roomId)?.getTimeLineEvent(state.eventId) ?: return state - val summary = event.annotations?.reactionsSummary - val quickReactions = quickEmojis.map { emoji -> - ToggleState(emoji, summary?.firstOrNull { it.key == emoji }?.addedByMe ?: false) - } - return state.copy(quickStates = quickReactions) + private fun observeReactions() { + if (room == null) return + RxRoom(room) + .liveAnnotationSummary(eventId) + .map { annotations -> + quickEmojis.map { emoji -> + ToggleState(emoji, annotations.reactionsSummary.firstOrNull { it.key == emoji }?.addedByMe + ?: false) + } + } + .execute { + copy(quickStates = it) + } } + fun didSelect(index: Int) = withState { - val isSelected = it.quickStates[index].isSelected + val selectedReaction = it.quickStates()?.get(index) ?: return@withState + val isSelected = selectedReaction.isSelected setState { - copy(result = ToggleState(it.quickStates[index].reaction, !isSelected)) + copy(result = ToggleState(selectedReaction.reaction, !isSelected)) } } diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/ViewReactionViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/ViewReactionViewModel.kt index 91305e13..3cfb4097 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/ViewReactionViewModel.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/ViewReactionViewModel.kt @@ -85,10 +85,8 @@ class ViewReactionViewModel @AssistedInject constructor(@Assisted .liveAnnotationSummary(eventId) .flatMapSingle { summaries -> Observable - .fromIterable(summaries) - .flatMapIterable { it.reactionsSummary - .filter { reactionAggregatedSummary -> isSingleEmoji(reactionAggregatedSummary.key) } - } + .fromIterable(summaries.reactionsSummary) + .filter { reactionAggregatedSummary -> isSingleEmoji(reactionAggregatedSummary.key) } .toReactionInfoList() } .execute { @@ -112,7 +110,6 @@ class ViewReactionViewModel @AssistedInject constructor(@Assisted timelineDateFormatter.formatMessageHour(localDate) ) } - } - .toList() + }.toList() } } \ No newline at end of file