Merge pull request #250 from vector-im/feature/fix_impure_reducers
Fix impure reducer and use live event
This commit is contained in:
commit
3960742f38
|
@ -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.Room
|
||||||
import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary
|
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.model.RoomSummary
|
||||||
|
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
import io.reactivex.Observable
|
import io.reactivex.Observable
|
||||||
|
|
||||||
class RxRoom(private val room: Room) {
|
class RxRoom(private val room: Room) {
|
||||||
|
@ -31,10 +32,14 @@ class RxRoom(private val room: Room) {
|
||||||
return room.getRoomMemberIdsLive().asObservable()
|
return room.getRoomMemberIdsLive().asObservable()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun liveAnnotationSummary(eventId: String): Observable<List<EventAnnotationsSummary>> {
|
fun liveAnnotationSummary(eventId: String): Observable<EventAnnotationsSummary> {
|
||||||
return room.getEventSummaryLive(eventId).asObservable()
|
return room.getEventSummaryLive(eventId).asObservable()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun liveTimelineEvent(eventId: String): Observable<TimelineEvent> {
|
||||||
|
return room.liveTimeLineEvent(eventId).asObservable()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Room.rx(): RxRoom {
|
fun Room.rx(): RxRoom {
|
||||||
|
|
|
@ -80,5 +80,5 @@ interface RelationService {
|
||||||
*/
|
*/
|
||||||
fun replyToMessage(eventReplied: Event, replyText: String): Cancelable?
|
fun replyToMessage(eventReplied: Event, replyText: String): Cancelable?
|
||||||
|
|
||||||
fun getEventSummaryLive(eventId: String): LiveData<List<EventAnnotationsSummary>>
|
fun getEventSummaryLive(eventId: String): LiveData<EventAnnotationsSummary>
|
||||||
}
|
}
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package im.vector.matrix.android.api.session.room.timeline
|
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.
|
* 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 getTimeLineEvent(eventId: String): TimelineEvent?
|
||||||
|
|
||||||
|
|
||||||
|
fun liveTimeLineEvent(eventId: String): LiveData<TimelineEvent>
|
||||||
}
|
}
|
|
@ -17,6 +17,7 @@ package im.vector.matrix.android.internal.session.room.relation
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.Transformations
|
||||||
import androidx.work.OneTimeWorkRequest
|
import androidx.work.OneTimeWorkRequest
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
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.message.MessageType
|
||||||
import im.vector.matrix.android.api.session.room.model.relation.RelationService
|
import im.vector.matrix.android.api.session.room.model.relation.RelationService
|
||||||
import im.vector.matrix.android.api.util.Cancelable
|
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.helper.addSendingEvent
|
||||||
import im.vector.matrix.android.internal.database.mapper.asDomain
|
import im.vector.matrix.android.internal.database.mapper.asDomain
|
||||||
import im.vector.matrix.android.internal.database.model.ChunkEntity
|
import im.vector.matrix.android.internal.database.model.ChunkEntity
|
||||||
|
@ -155,15 +157,13 @@ internal class DefaultRelationService @Inject constructor(private val context: C
|
||||||
return workRequest
|
return workRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getEventSummaryLive(eventId: String): LiveData<List<EventAnnotationsSummary>> {
|
override fun getEventSummaryLive(eventId: String): LiveData<EventAnnotationsSummary> {
|
||||||
return monarchy.findAllMappedWithChanges(
|
val liveEntity = RealmLiveData(monarchy.realmConfiguration) { realm ->
|
||||||
{ realm ->
|
EventAnnotationsSummaryEntity.where(realm, eventId)
|
||||||
EventAnnotationsSummaryEntity.where(realm, eventId)
|
}
|
||||||
},
|
return Transformations.map(liveEntity) { realmResults ->
|
||||||
{
|
realmResults.firstOrNull()?.asDomain() ?: EventAnnotationsSummary(eventId, emptyList(), null)
|
||||||
it.asDomain()
|
}
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -16,10 +16,14 @@
|
||||||
|
|
||||||
package im.vector.matrix.android.internal.session.room.timeline
|
package im.vector.matrix.android.internal.session.room.timeline
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MediatorLiveData
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineService
|
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.model.EventEntity
|
||||||
import im.vector.matrix.android.internal.database.query.where
|
import im.vector.matrix.android.internal.database.query.where
|
||||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||||
|
@ -47,5 +51,27 @@ internal class DefaultTimelineService @Inject constructor(private val roomId: St
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun liveTimeLineEvent(eventId: String): LiveData<TimelineEvent> {
|
||||||
|
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<TimelineEvent>()
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -15,10 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package im.vector.riotredesign.features.home.room.detail.timeline.action
|
package im.vector.riotredesign.features.home.room.detail.timeline.action
|
||||||
|
|
||||||
import com.airbnb.mvrx.FragmentViewModelContext
|
import com.airbnb.mvrx.*
|
||||||
import com.airbnb.mvrx.MvRxState
|
|
||||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
|
||||||
import com.airbnb.mvrx.ViewModelContext
|
|
||||||
import com.squareup.inject.assisted.Assisted
|
import com.squareup.inject.assisted.Assisted
|
||||||
import com.squareup.inject.assisted.AssistedInject
|
import com.squareup.inject.assisted.AssistedInject
|
||||||
import im.vector.matrix.android.api.session.Session
|
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.MessageTextContent
|
||||||
import im.vector.matrix.android.api.session.room.model.message.MessageType
|
import im.vector.matrix.android.api.session.room.model.message.MessageType
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
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.core.platform.VectorViewModel
|
||||||
import im.vector.riotredesign.features.home.room.detail.timeline.format.NoticeEventFormatter
|
import im.vector.riotredesign.features.home.room.detail.timeline.format.NoticeEventFormatter
|
||||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
|
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
|
||||||
|
@ -37,27 +34,30 @@ import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
val dateFormat = SimpleDateFormat("EEE, d MMM yyyy HH:mm", Locale.getDefault())
|
|
||||||
|
|
||||||
data class MessageActionState(
|
data class MessageActionState(
|
||||||
val roomId: String,
|
val roomId: String,
|
||||||
val eventId: String,
|
val eventId: String,
|
||||||
val informationData: MessageInformationData,
|
val informationData: MessageInformationData,
|
||||||
val timelineEvent: TimelineEvent?
|
val timelineEvent: Async<TimelineEvent> = Uninitialized
|
||||||
) : MvRxState {
|
) : 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 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? {
|
fun messageBody(eventHtmlRenderer: EventHtmlRenderer?, noticeEventFormatter: NoticeEventFormatter?): CharSequence? {
|
||||||
return when (timelineEvent?.root?.getClearType()) {
|
return when (timelineEvent()?.root?.getClearType()) {
|
||||||
EventType.MESSAGE -> {
|
EventType.MESSAGE -> {
|
||||||
val messageContent: MessageContent? = timelineEvent.annotations?.editSummary?.aggregatedContent?.toModel()
|
val messageContent: MessageContent? = timelineEvent()?.annotations?.editSummary?.aggregatedContent?.toModel()
|
||||||
?: timelineEvent.root.getClearContent().toModel()
|
?: timelineEvent()?.root?.getClearContent().toModel()
|
||||||
if (messageContent is MessageTextContent && messageContent.format == MessageType.FORMAT_MATRIX_HTML) {
|
if (messageContent is MessageTextContent && messageContent.format == MessageType.FORMAT_MATRIX_HTML) {
|
||||||
eventHtmlRenderer?.render(messageContent.formattedBody
|
eventHtmlRenderer?.render(messageContent.formattedBody
|
||||||
?: messageContent.body)
|
?: messageContent.body)
|
||||||
|
@ -72,7 +72,7 @@ data class MessageActionState(
|
||||||
EventType.CALL_INVITE,
|
EventType.CALL_INVITE,
|
||||||
EventType.CALL_HANGUP,
|
EventType.CALL_HANGUP,
|
||||||
EventType.CALL_ANSWER -> {
|
EventType.CALL_ANSWER -> {
|
||||||
noticeEventFormatter?.format(timelineEvent)
|
timelineEvent()?.let { noticeEventFormatter?.format(it) }
|
||||||
}
|
}
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
@ -85,10 +85,14 @@ data class MessageActionState(
|
||||||
class MessageActionsViewModel @AssistedInject constructor(@Assisted
|
class MessageActionsViewModel @AssistedInject constructor(@Assisted
|
||||||
initialState: MessageActionState,
|
initialState: MessageActionState,
|
||||||
private val eventHtmlRenderer: EventHtmlRenderer,
|
private val eventHtmlRenderer: EventHtmlRenderer,
|
||||||
private val session: Session,
|
session: Session,
|
||||||
private val noticeEventFormatter: NoticeEventFormatter
|
private val noticeEventFormatter: NoticeEventFormatter
|
||||||
) : VectorViewModel<MessageActionState>(initialState) {
|
) : VectorViewModel<MessageActionState>(initialState) {
|
||||||
|
|
||||||
|
|
||||||
|
private val eventId = initialState.eventId
|
||||||
|
private val room = session.getRoom(initialState.roomId)
|
||||||
|
|
||||||
@AssistedInject.Factory
|
@AssistedInject.Factory
|
||||||
interface Factory {
|
interface Factory {
|
||||||
fun create(initialState: MessageActionState): MessageActionsViewModel
|
fun create(initialState: MessageActionState): MessageActionsViewModel
|
||||||
|
@ -101,18 +105,19 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
|
||||||
return fragment.messageActionViewModelFactory.create(state)
|
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? {
|
fun resolveBody(state: MessageActionState): CharSequence? {
|
||||||
|
|
|
@ -55,7 +55,8 @@ class MessageMenuFragment : VectorBaseFragment() {
|
||||||
val inflater = LayoutInflater.from(linearLayout.context)
|
val inflater = LayoutInflater.from(linearLayout.context)
|
||||||
linearLayout.removeAllViews()
|
linearLayout.removeAllViews()
|
||||||
var insertIndex = 0
|
var insertIndex = 0
|
||||||
state.actions.forEachIndexed { index, action ->
|
val actions = state.actions()
|
||||||
|
actions?.forEachIndexed { index, action ->
|
||||||
inflateActionView(action, inflater, linearLayout)?.let {
|
inflateActionView(action, inflater, linearLayout)?.let {
|
||||||
it.setOnClickListener {
|
it.setOnClickListener {
|
||||||
interactionListener?.didSelectMenuAction(action)
|
interactionListener?.didSelectMenuAction(action)
|
||||||
|
@ -63,7 +64,7 @@ class MessageMenuFragment : VectorBaseFragment() {
|
||||||
linearLayout.addView(it, insertIndex)
|
linearLayout.addView(it, insertIndex)
|
||||||
insertIndex++
|
insertIndex++
|
||||||
if (addSeparators) {
|
if (addSeparators) {
|
||||||
if (index < state.actions.size - 1) {
|
if (index < actions.size - 1) {
|
||||||
linearLayout.addView(inflateSeparatorView(), insertIndex)
|
linearLayout.addView(inflateSeparatorView(), insertIndex)
|
||||||
insertIndex++
|
insertIndex++
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,10 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package im.vector.riotredesign.features.home.room.detail.timeline.action
|
package im.vector.riotredesign.features.home.room.detail.timeline.action
|
||||||
|
|
||||||
import com.airbnb.mvrx.FragmentViewModelContext
|
import com.airbnb.mvrx.*
|
||||||
import com.airbnb.mvrx.MvRxState
|
|
||||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
|
||||||
import com.airbnb.mvrx.ViewModelContext
|
|
||||||
import com.squareup.inject.assisted.Assisted
|
import com.squareup.inject.assisted.Assisted
|
||||||
import com.squareup.inject.assisted.AssistedInject
|
import com.squareup.inject.assisted.AssistedInject
|
||||||
import im.vector.matrix.android.api.session.Session
|
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.model.message.MessageType
|
||||||
import im.vector.matrix.android.api.session.room.send.SendState
|
import im.vector.matrix.android.api.session.room.send.SendState
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
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.R
|
||||||
import im.vector.riotredesign.core.platform.VectorViewModel
|
import im.vector.riotredesign.core.platform.VectorViewModel
|
||||||
import im.vector.riotredesign.core.resources.StringProvider
|
import im.vector.riotredesign.core.resources.StringProvider
|
||||||
|
@ -44,7 +42,7 @@ data class MessageMenuState(
|
||||||
val roomId: String,
|
val roomId: String,
|
||||||
val eventId: String,
|
val eventId: String,
|
||||||
val informationData: MessageInformationData,
|
val informationData: MessageInformationData,
|
||||||
val actions: List<SimpleAction> = emptyList()
|
val actions: Async<List<SimpleAction>> = Uninitialized
|
||||||
) : MvRxState {
|
) : MvRxState {
|
||||||
|
|
||||||
constructor(args: TimelineEventFragmentArgs) : this(roomId = args.roomId, eventId = args.eventId, informationData = args.informationData)
|
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
|
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<MessageMenuViewModel, MessageMenuState> {
|
companion object : MvRxViewModelFactory<MessageMenuViewModel, MessageMenuState> {
|
||||||
|
|
||||||
const val ACTION_ADD_REACTION = "add_reaction"
|
const val ACTION_ADD_REACTION = "add_reaction"
|
||||||
|
@ -87,13 +91,23 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
setState { reduceState(this) }
|
observeEvent()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun reduceState(state: MessageMenuState): MessageMenuState {
|
private fun observeEvent() {
|
||||||
val event = session.getRoom(state.roomId)?.getTimeLineEvent(state.eventId) ?: return state
|
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<SimpleAction> {
|
||||||
|
|
||||||
|
val messageContent: MessageContent? = event.annotations?.editSummary?.aggregatedContent.toModel()
|
||||||
?: event.root.getClearContent().toModel()
|
?: event.root.getClearContent().toModel()
|
||||||
val type = messageContent?.type
|
val type = messageContent?.type
|
||||||
|
|
||||||
|
@ -114,7 +128,7 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M
|
||||||
//TODO is downloading attachement?
|
//TODO is downloading attachement?
|
||||||
|
|
||||||
if (canReact(event, messageContent)) {
|
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)) {
|
if (canCopy(type)) {
|
||||||
//TODO copy images? html? see ClipBoard
|
//TODO copy images? html? see ClipBoard
|
||||||
|
@ -122,23 +136,23 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canReply(event, messageContent)) {
|
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)) {
|
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)) {
|
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)) {
|
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)) {
|
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)) {
|
if (canShare(type)) {
|
||||||
|
@ -162,20 +176,20 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M
|
||||||
|
|
||||||
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.toContent()).toString(4)))
|
||||||
if (event.isEncrypted()) {
|
if (event.isEncrypted()) {
|
||||||
val decryptedContent = event.root.mClearEvent?.toContent()?.let {
|
val decryptedContent = event.root.mClearEvent.toContent()?.let {
|
||||||
JSONObject(it).toString(4)
|
JSONObject(it).toString(4)
|
||||||
} ?: stringProvider.getString(R.string.encryption_information_decryption_error)
|
} ?: 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(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) {
|
if (session.sessionParams.credentials.userId != event.root.senderId && event.root.getClearType() == EventType.MESSAGE) {
|
||||||
//not sent by me
|
//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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ class QuickReactionFragment : VectorBaseFragment() {
|
||||||
injector.inject(this)
|
injector.inject(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
lateinit var textViews: List<TextView>
|
private lateinit var textViews: List<TextView>
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
@ -63,7 +63,8 @@ class QuickReactionFragment : VectorBaseFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun invalidate() = withState(viewModel) {
|
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].text = qs.reaction
|
||||||
textViews[index].alpha = if (qs.isSelected) 0.2f else 1f
|
textViews[index].alpha = if (qs.isSelected) 0.2f else 1f
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,13 +15,11 @@
|
||||||
*/
|
*/
|
||||||
package im.vector.riotredesign.features.home.room.detail.timeline.action
|
package im.vector.riotredesign.features.home.room.detail.timeline.action
|
||||||
|
|
||||||
import com.airbnb.mvrx.FragmentViewModelContext
|
import com.airbnb.mvrx.*
|
||||||
import com.airbnb.mvrx.MvRxState
|
|
||||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
|
||||||
import com.airbnb.mvrx.ViewModelContext
|
|
||||||
import com.squareup.inject.assisted.Assisted
|
import com.squareup.inject.assisted.Assisted
|
||||||
import com.squareup.inject.assisted.AssistedInject
|
import com.squareup.inject.assisted.AssistedInject
|
||||||
import im.vector.matrix.android.api.session.Session
|
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.core.platform.VectorViewModel
|
||||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
|
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
|
||||||
|
|
||||||
|
@ -37,7 +35,7 @@ data class QuickReactionState(
|
||||||
val roomId: String,
|
val roomId: String,
|
||||||
val eventId: String,
|
val eventId: String,
|
||||||
val informationData: MessageInformationData,
|
val informationData: MessageInformationData,
|
||||||
val quickStates: List<ToggleState> = emptyList(),
|
val quickStates: Async<List<ToggleState>> = Uninitialized,
|
||||||
val result: ToggleState? = null
|
val result: ToggleState? = null
|
||||||
/** Pair of 'clickedOn' and current toggles state*/
|
/** Pair of 'clickedOn' and current toggles state*/
|
||||||
) : MvRxState {
|
) : MvRxState {
|
||||||
|
@ -56,6 +54,9 @@ class QuickReactionViewModel @AssistedInject constructor(@Assisted initialState:
|
||||||
fun create(initialState: QuickReactionState): QuickReactionViewModel
|
fun create(initialState: QuickReactionState): QuickReactionViewModel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val room = session.getRoom(initialState.roomId)
|
||||||
|
private val eventId = initialState.eventId
|
||||||
|
|
||||||
companion object : MvRxViewModelFactory<QuickReactionViewModel, QuickReactionState> {
|
companion object : MvRxViewModelFactory<QuickReactionViewModel, QuickReactionState> {
|
||||||
|
|
||||||
val quickEmojis = listOf("👍", "👎", "😄", "🎉", "😕", "❤️", "🚀", "👀")
|
val quickEmojis = listOf("👍", "👎", "😄", "🎉", "😕", "❤️", "🚀", "👀")
|
||||||
|
@ -67,22 +68,30 @@ class QuickReactionViewModel @AssistedInject constructor(@Assisted initialState:
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
setState { reduceState(this) }
|
observeReactions()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun reduceState(state: QuickReactionState): QuickReactionState {
|
private fun observeReactions() {
|
||||||
val event = session.getRoom(state.roomId)?.getTimeLineEvent(state.eventId) ?: return state
|
if (room == null) return
|
||||||
val summary = event.annotations?.reactionsSummary
|
RxRoom(room)
|
||||||
val quickReactions = quickEmojis.map { emoji ->
|
.liveAnnotationSummary(eventId)
|
||||||
ToggleState(emoji, summary?.firstOrNull { it.key == emoji }?.addedByMe ?: false)
|
.map { annotations ->
|
||||||
}
|
quickEmojis.map { emoji ->
|
||||||
return state.copy(quickStates = quickReactions)
|
ToggleState(emoji, annotations.reactionsSummary.firstOrNull { it.key == emoji }?.addedByMe
|
||||||
|
?: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.execute {
|
||||||
|
copy(quickStates = it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun didSelect(index: Int) = withState {
|
fun didSelect(index: Int) = withState {
|
||||||
val isSelected = it.quickStates[index].isSelected
|
val selectedReaction = it.quickStates()?.get(index) ?: return@withState
|
||||||
|
val isSelected = selectedReaction.isSelected
|
||||||
setState {
|
setState {
|
||||||
copy(result = ToggleState(it.quickStates[index].reaction, !isSelected))
|
copy(result = ToggleState(selectedReaction.reaction, !isSelected))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -85,10 +85,8 @@ class ViewReactionViewModel @AssistedInject constructor(@Assisted
|
||||||
.liveAnnotationSummary(eventId)
|
.liveAnnotationSummary(eventId)
|
||||||
.flatMapSingle { summaries ->
|
.flatMapSingle { summaries ->
|
||||||
Observable
|
Observable
|
||||||
.fromIterable(summaries)
|
.fromIterable(summaries.reactionsSummary)
|
||||||
.flatMapIterable { it.reactionsSummary
|
.filter { reactionAggregatedSummary -> isSingleEmoji(reactionAggregatedSummary.key) }
|
||||||
.filter { reactionAggregatedSummary -> isSingleEmoji(reactionAggregatedSummary.key) }
|
|
||||||
}
|
|
||||||
.toReactionInfoList()
|
.toReactionInfoList()
|
||||||
}
|
}
|
||||||
.execute {
|
.execute {
|
||||||
|
@ -112,7 +110,6 @@ class ViewReactionViewModel @AssistedInject constructor(@Assisted
|
||||||
timelineDateFormatter.formatMessageHour(localDate)
|
timelineDateFormatter.formatMessageHour(localDate)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}.toList()
|
||||||
.toList()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue