Merge pull request #214 from vector-im/feature/update_quick_reactions

Feature/ Update quick reactions
This commit is contained in:
Valere 2019-06-25 15:47:17 +02:00 committed by GitHub
commit 8fe0bd5abe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 144 additions and 411 deletions

View File

@ -63,19 +63,6 @@ interface RelationService {
fun undoReaction(reaction: String, targetEventId: String, myUserId: String)//: Cancelable fun undoReaction(reaction: String, targetEventId: String, myUserId: String)//: Cancelable




/**
* Update a quick reaction (toggle).
* If you have reacted with agree and then you click on disagree, this call will delete(redact)
* the disagree and add the agree
* If you click on a reaction that you already reacted with, it will undo it
* @param reaction the reaction (preferably emoji)
* @param oppositeReaction the opposite reaction(preferably emoji)
* @param targetEventId the id of the event being reacted
* @param myUserId used to know if a reaction event was made by the user
*/
fun updateQuickReaction(reaction: String, oppositeReaction: String, targetEventId: String, myUserId: String)


/** /**
* Edit a text message body. Limited to "m.text" contentType * Edit a text message body. Limited to "m.text" contentType
* @param targetEventId The event to edit * @param targetEventId The event to edit

View File

@ -29,7 +29,6 @@ import im.vector.matrix.android.internal.session.room.read.DefaultReadService
import im.vector.matrix.android.internal.session.room.read.SetReadMarkersTask import im.vector.matrix.android.internal.session.room.read.SetReadMarkersTask
import im.vector.matrix.android.internal.session.room.relation.DefaultRelationService import im.vector.matrix.android.internal.session.room.relation.DefaultRelationService
import im.vector.matrix.android.internal.session.room.relation.FindReactionEventForUndoTask import im.vector.matrix.android.internal.session.room.relation.FindReactionEventForUndoTask
import im.vector.matrix.android.internal.session.room.relation.UpdateQuickReactionTask
import im.vector.matrix.android.internal.session.room.send.DefaultSendService import im.vector.matrix.android.internal.session.room.send.DefaultSendService
import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory
import im.vector.matrix.android.internal.session.room.state.DefaultStateService import im.vector.matrix.android.internal.session.room.state.DefaultStateService
@ -51,7 +50,6 @@ internal class RoomFactory(private val monarchy: Monarchy,
private val setReadMarkersTask: SetReadMarkersTask, private val setReadMarkersTask: SetReadMarkersTask,
private val cryptoService: CryptoService, private val cryptoService: CryptoService,
private val findReactionEventForUndoTask: FindReactionEventForUndoTask, private val findReactionEventForUndoTask: FindReactionEventForUndoTask,
private val updateQuickReactionTask: UpdateQuickReactionTask,
private val joinRoomTask: JoinRoomTask, private val joinRoomTask: JoinRoomTask,
private val leaveRoomTask: LeaveRoomTask) { private val leaveRoomTask: LeaveRoomTask) {


@ -64,7 +62,7 @@ internal class RoomFactory(private val monarchy: Monarchy,
val stateService = DefaultStateService(roomId, taskExecutor, sendStateTask) val stateService = DefaultStateService(roomId, taskExecutor, sendStateTask)
val roomMembersService = DefaultMembershipService(roomId, monarchy, taskExecutor, loadRoomMembersTask, inviteTask, joinRoomTask, leaveRoomTask) val roomMembersService = DefaultMembershipService(roomId, monarchy, taskExecutor, loadRoomMembersTask, inviteTask, joinRoomTask, leaveRoomTask)
val readService = DefaultReadService(roomId, monarchy, taskExecutor, setReadMarkersTask) val readService = DefaultReadService(roomId, monarchy, taskExecutor, setReadMarkersTask)
val reactionService = DefaultRelationService(roomId, eventFactory, findReactionEventForUndoTask, updateQuickReactionTask, monarchy, taskExecutor) val reactionService = DefaultRelationService(roomId, eventFactory, findReactionEventForUndoTask, monarchy, taskExecutor)


return DefaultRoom( return DefaultRoom(
roomId, roomId,

View File

@ -32,9 +32,7 @@ import im.vector.matrix.android.internal.session.room.prune.PruneEventTask
import im.vector.matrix.android.internal.session.room.read.DefaultSetReadMarkersTask import im.vector.matrix.android.internal.session.room.read.DefaultSetReadMarkersTask
import im.vector.matrix.android.internal.session.room.read.SetReadMarkersTask import im.vector.matrix.android.internal.session.room.read.SetReadMarkersTask
import im.vector.matrix.android.internal.session.room.relation.DefaultFindReactionEventForUndoTask import im.vector.matrix.android.internal.session.room.relation.DefaultFindReactionEventForUndoTask
import im.vector.matrix.android.internal.session.room.relation.DefaultUpdateQuickReactionTask
import im.vector.matrix.android.internal.session.room.relation.FindReactionEventForUndoTask import im.vector.matrix.android.internal.session.room.relation.FindReactionEventForUndoTask
import im.vector.matrix.android.internal.session.room.relation.UpdateQuickReactionTask
import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory
import im.vector.matrix.android.internal.session.room.send.LocalEchoUpdater import im.vector.matrix.android.internal.session.room.send.LocalEchoUpdater
import im.vector.matrix.android.internal.session.room.state.DefaultSendStateTask import im.vector.matrix.android.internal.session.room.state.DefaultSendStateTask
@ -82,7 +80,7 @@ class RoomModule {
} }


scope(DefaultSession.SCOPE) { scope(DefaultSession.SCOPE) {
RoomFactory(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get()) RoomFactory(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get())
} }


scope(DefaultSession.SCOPE) { scope(DefaultSession.SCOPE) {
@ -109,10 +107,6 @@ class RoomModule {
DefaultFindReactionEventForUndoTask(get()) as FindReactionEventForUndoTask DefaultFindReactionEventForUndoTask(get()) as FindReactionEventForUndoTask
} }


scope(DefaultSession.SCOPE) {
DefaultUpdateQuickReactionTask(get()) as UpdateQuickReactionTask
}

scope(DefaultSession.SCOPE) { scope(DefaultSession.SCOPE) {
DefaultPruneEventTask(get()) as PruneEventTask DefaultPruneEventTask(get()) as PruneEventTask
} }

View File

@ -46,7 +46,6 @@ import timber.log.Timber
internal class DefaultRelationService(private val roomId: String, internal class DefaultRelationService(private val roomId: String,
private val eventFactory: LocalEchoEventFactory, private val eventFactory: LocalEchoEventFactory,
private val findReactionEventForUndoTask: FindReactionEventForUndoTask, private val findReactionEventForUndoTask: FindReactionEventForUndoTask,
private val updateQuickReactionTask: UpdateQuickReactionTask,
private val monarchy: Monarchy, private val monarchy: Monarchy,
private val taskExecutor: TaskExecutor) private val taskExecutor: TaskExecutor)
: RelationService { : RelationService {
@ -105,31 +104,6 @@ internal class DefaultRelationService(private val roomId: String,
} }




override fun updateQuickReaction(reaction: String, oppositeReaction: String, targetEventId: String, myUserId: String) {

val params = UpdateQuickReactionTask.Params(
roomId,
targetEventId,
reaction,
oppositeReaction,
myUserId
)

updateQuickReactionTask.configureWith(params)
.dispatchTo(object : MatrixCallback<UpdateQuickReactionTask.Result> {
override fun onSuccess(data: UpdateQuickReactionTask.Result) {
data.reactionToAdd?.also { sendReaction(it, targetEventId) }
data.reactionToRedact.forEach {
val redactEvent = eventFactory.createRedactEvent(roomId, it, null).also {
saveLocalEcho(it)
}
val redactWork = createRedactEventWork(redactEvent, it, null)
TimelineSendEventWorkCommon.postWork(roomId, redactWork)
}
}
})
.executeBy(taskExecutor)
}


private fun buildWorkIdentifier(identifier: String): String { private fun buildWorkIdentifier(identifier: String): String {
return "${roomId}_$identifier" return "${roomId}_$identifier"

View File

@ -1,89 +0,0 @@
/*
* Copyright 2019 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.matrix.android.internal.session.room.relation

import arrow.core.Try
import com.zhuinden.monarchy.Monarchy
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.ReactionAggregatedSummaryEntityFields
import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.task.Task
import io.realm.Realm


internal interface UpdateQuickReactionTask : Task<UpdateQuickReactionTask.Params, UpdateQuickReactionTask.Result> {

data class Params(
val roomId: String,
val eventId: String,
val reaction: String,
val oppositeReaction: String,
val myUserId: String
)

data class Result(
val reactionToAdd: String?,
val reactionToRedact: List<String>
)
}

internal class DefaultUpdateQuickReactionTask(private val monarchy: Monarchy) : UpdateQuickReactionTask {
override suspend fun execute(params: UpdateQuickReactionTask.Params): Try<UpdateQuickReactionTask.Result> {
return Try {
var res: Pair<String?, List<String>?>? = null
monarchy.doWithRealm { realm ->
res = updateQuickReaction(realm, params.reaction, params.oppositeReaction, params.eventId, params.myUserId)
}
UpdateQuickReactionTask.Result(res?.first, res?.second ?: emptyList())
}
}


private fun updateQuickReaction(realm: Realm, reaction: String, oppositeReaction: String, eventId: String, myUserId: String): Pair<String?, List<String>?> {
//the emoji reaction has been selected, we need to check if we have reacted it or not
val existingSummary = EventAnnotationsSummaryEntity.where(realm, eventId).findFirst()
?: return Pair(reaction, null)

//Ok there is already reactions on this event, have we reacted to it
val aggregationForReaction = existingSummary.reactionsSummary.where()
.equalTo(ReactionAggregatedSummaryEntityFields.KEY, reaction)
.findFirst()
val aggregationForOppositeReaction = existingSummary.reactionsSummary.where()
.equalTo(ReactionAggregatedSummaryEntityFields.KEY, oppositeReaction)
.findFirst()

if (aggregationForReaction == null || !aggregationForReaction.addedByMe) {
//i haven't yet reacted to it, so need to add it, but do I need to redact the opposite?
val toRedact = aggregationForOppositeReaction?.sourceEvents?.mapNotNull {
//find source event
val entity = EventEntity.where(realm, it).findFirst()
if (entity?.sender == myUserId) entity.eventId else null
}
return Pair(reaction, toRedact)
} else {
//I already added it, so i need to undo it (like a toggle)
// find all m.redaction coming from me to readact them
val toRedact = aggregationForReaction.sourceEvents.mapNotNull {
//find source event
val entity = EventEntity.where(realm, it).findFirst()
if (entity?.sender == myUserId) entity.eventId else null
}
return Pair(null, toRedact)
}

}
}

View File

@ -30,7 +30,7 @@ sealed class RoomDetailActions {
data class SendReaction(val reaction: String, val targetEventId: String) : RoomDetailActions() data class SendReaction(val reaction: String, val targetEventId: String) : RoomDetailActions()
data class RedactAction(val targetEventId: String, val reason: String? = "") : RoomDetailActions() data class RedactAction(val targetEventId: String, val reason: String? = "") : RoomDetailActions()
data class UndoReaction(val targetEventId: String, val key: String, val reason: String? = "") : RoomDetailActions() data class UndoReaction(val targetEventId: String, val key: String, val reason: String? = "") : RoomDetailActions()
data class UpdateQuickReactAction(val targetEventId: String, val selectedReaction: String, val opposite: String) : RoomDetailActions() data class UpdateQuickReactAction(val targetEventId: String, val selectedReaction: String, val add: Boolean) : RoomDetailActions()
data class ShowEditHistoryAction(val event: String, val editAggregatedSummary: EditAggregatedSummary) : RoomDetailActions() data class ShowEditHistoryAction(val event: String, val editAggregatedSummary: EditAggregatedSummary) : RoomDetailActions()
data class NavigateToEvent(val eventId: String, val position: Int?) : RoomDetailActions() data class NavigateToEvent(val eventId: String, val position: Int?) : RoomDetailActions()
object AcceptInvite : RoomDetailActions() object AcceptInvite : RoomDetailActions()

View File

@ -708,9 +708,9 @@ class RoomDetailFragment :
.show() .show()
} }
MessageMenuViewModel.ACTION_QUICK_REACT -> { MessageMenuViewModel.ACTION_QUICK_REACT -> {
//eventId,ClickedOn,Opposite //eventId,ClickedOn,Add
(actionData.data as? Triple<String, String, String>)?.let { (eventId, clickedOn, opposite) -> (actionData.data as? Triple<String, String, Boolean>)?.let { (eventId, clickedOn, add) ->
roomDetailViewModel.process(RoomDetailActions.UpdateQuickReactAction(eventId, clickedOn, opposite)) roomDetailViewModel.process(RoomDetailActions.UpdateQuickReactAction(eventId, clickedOn, add))
} }
} }
MessageMenuViewModel.ACTION_EDIT -> { MessageMenuViewModel.ACTION_EDIT -> {

View File

@ -341,7 +341,11 @@ class RoomDetailViewModel(initialState: RoomDetailViewState,




private fun handleUpdateQuickReaction(action: RoomDetailActions.UpdateQuickReactAction) { private fun handleUpdateQuickReaction(action: RoomDetailActions.UpdateQuickReactAction) {
room.updateQuickReaction(action.selectedReaction, action.opposite, action.targetEventId, session.sessionParams.credentials.userId) if (action.add) {
room.sendReaction(action.selectedReaction, action.targetEventId)
} else {
room.undoReaction(action.selectedReaction, action.targetEventId, session.sessionParams.credentials.userId)
}
} }


private fun handleSendMedia(action: RoomDetailActions.SendMedia) { private fun handleSendMedia(action: RoomDetailActions.SendMedia) {

View File

@ -94,8 +94,9 @@ class MessageActionsBottomSheet : BaseMvRxBottomSheetDialog() {
.commit() .commit()
} }
quickReactionFragment.interactionListener = object : QuickReactionFragment.InteractionListener { quickReactionFragment.interactionListener = object : QuickReactionFragment.InteractionListener {
override fun didQuickReactWith(clikedOn: String, opposite: String, reactions: List<String>, eventId: String) {
actionHandlerModel.fireAction(MessageMenuViewModel.ACTION_QUICK_REACT, Triple(eventId, clikedOn, opposite)) override fun didQuickReactWith(clikedOn: String, add: Boolean, eventId: String) {
actionHandlerModel.fireAction(MessageMenuViewModel.ACTION_QUICK_REACT, Triple(eventId, clikedOn, add))
dismiss() dismiss()
} }
} }

View File

@ -21,9 +21,6 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.TextView import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.transition.TransitionManager
import butterknife.BindView
import butterknife.ButterKnife import butterknife.ButterKnife
import com.airbnb.mvrx.BaseMvRxFragment import com.airbnb.mvrx.BaseMvRxFragment
import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.MvRx
@ -31,6 +28,7 @@ import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
import im.vector.riotredesign.EmojiCompatFontProvider import im.vector.riotredesign.EmojiCompatFontProvider
import im.vector.riotredesign.R import im.vector.riotredesign.R
import kotlinx.android.synthetic.main.adapter_item_action_quick_reaction.*
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject


/** /**
@ -40,21 +38,6 @@ class QuickReactionFragment : BaseMvRxFragment() {


private val viewModel: QuickReactionViewModel by fragmentViewModel(QuickReactionViewModel::class) private val viewModel: QuickReactionViewModel by fragmentViewModel(QuickReactionViewModel::class)


@BindView(R.id.root_layout)
lateinit var rootLayout: ConstraintLayout

@BindView(R.id.quick_react_1_text)
lateinit var quickReact1Text: TextView

@BindView(R.id.quick_react_2_text)
lateinit var quickReact2Text: TextView

@BindView(R.id.quick_react_3_text)
lateinit var quickReact3Text: TextView

@BindView(R.id.quick_react_4_text)
lateinit var quickReact4Text: TextView

var interactionListener: InteractionListener? = null var interactionListener: InteractionListener? = null


val fontProvider by inject<EmojiCompatFontProvider>() val fontProvider by inject<EmojiCompatFontProvider>()
@ -65,77 +48,38 @@ class QuickReactionFragment : BaseMvRxFragment() {
return view return view
} }


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)


quickReact1Text.text = QuickReactionViewModel.agreePositive textViews = listOf(quickReaction0, quickReaction1, quickReaction2, quickReaction3,
quickReact2Text.text = QuickReactionViewModel.agreeNegative quickReaction4, quickReaction5, quickReaction6, quickReaction7)
quickReact3Text.text = QuickReactionViewModel.likePositive
quickReact4Text.text = QuickReactionViewModel.likeNegative


listOf(quickReact1Text, quickReact2Text, quickReact3Text, quickReact4Text).forEach { textViews.forEachIndexed { index, textView ->
it.typeface = fontProvider.typeface ?: Typeface.DEFAULT textView.typeface = fontProvider.typeface ?: Typeface.DEFAULT
} textView.setOnClickListener {

viewModel.didSelect(index)
//configure click listeners }
quickReact1Text.setOnClickListener {
viewModel.toggleAgree(true)
}
quickReact2Text.setOnClickListener {
viewModel.toggleAgree(false)
}
quickReact3Text.setOnClickListener {
viewModel.toggleLike(true)
}
quickReact4Text.setOnClickListener {
viewModel.toggleLike(false)
} }


} }


override fun invalidate() = withState(viewModel) { override fun invalidate() = withState(viewModel) {


TransitionManager.beginDelayedTransition(rootLayout) it.quickStates.forEachIndexed { index, qs ->
when (it.agreeTrigleState) { textViews[index].text = qs.reaction
TriggleState.NONE -> { textViews[index].alpha = if (qs.isSelected) 0.2f else 1f
quickReact1Text.alpha = 1f
quickReact2Text.alpha = 1f
}
TriggleState.FIRST -> {
quickReact1Text.alpha = 1f
quickReact2Text.alpha = 0.2f

}
TriggleState.SECOND -> {
quickReact1Text.alpha = 0.2f
quickReact2Text.alpha = 1f
}
}
when (it.likeTriggleState) {
TriggleState.NONE -> {
quickReact3Text.alpha = 1f
quickReact4Text.alpha = 1f
}
TriggleState.FIRST -> {
quickReact3Text.alpha = 1f
quickReact4Text.alpha = 0.2f

}
TriggleState.SECOND -> {
quickReact3Text.alpha = 0.2f
quickReact4Text.alpha = 1f
}
} }


if (it.selectionResult != null) { if (it.result != null) {
val clikedOn = it.selectionResult.first interactionListener?.didQuickReactWith(it.result.reaction, it.result.isSelected, it.eventId)
interactionListener?.didQuickReactWith(clikedOn, QuickReactionViewModel.getOpposite(clikedOn)
?: "", it.selectionResult.second, it.eventId)
} }

} }


interface InteractionListener { interface InteractionListener {
fun didQuickReactWith(clikedOn: String, opposite: String, reactions: List<String>, eventId: String) fun didQuickReactWith(clikedOn: String, add: Boolean, eventId: String)
} }


companion object { companion object {

View File

@ -25,18 +25,16 @@ import org.koin.android.ext.android.get
/** /**
* Quick reactions state, it's a toggle with 3rd state * Quick reactions state, it's a toggle with 3rd state
*/ */
enum class TriggleState { data class ToggleState(
NONE, val reaction: String,
FIRST, val isSelected: Boolean
SECOND )
}


data class QuickReactionState( data class QuickReactionState(
val agreeTrigleState: TriggleState = TriggleState.NONE, val quickStates: List<ToggleState>,
val likeTriggleState: TriggleState = TriggleState.NONE, val eventId: String = "",
/** Pair of 'clickedOn' and current toggles state*/ val result: ToggleState? = null
val selectionResult: Pair<String, List<String>>? = null, ) : MvRxState
val eventId: String = "") : MvRxState


/** /**
* Quick reaction view model * Quick reaction view model
@ -44,107 +42,28 @@ data class QuickReactionState(
class QuickReactionViewModel(initialState: QuickReactionState) : VectorViewModel<QuickReactionState>(initialState) { class QuickReactionViewModel(initialState: QuickReactionState) : VectorViewModel<QuickReactionState>(initialState) {




fun toggleAgree(isFirst: Boolean) = withState { fun didSelect(index: Int) = withState {
if (isFirst) { val isSelected = it.quickStates[index].isSelected
setState { setState {
val newTriggle = if (it.agreeTrigleState == TriggleState.FIRST) TriggleState.NONE else TriggleState.FIRST copy(result = ToggleState(it.quickStates[index].reaction, !isSelected))
copy(
agreeTrigleState = newTriggle,
selectionResult = Pair(agreePositive, getReactions(this, newTriggle, null))
)
}
} else {
setState {
val newTriggle = if (it.agreeTrigleState == TriggleState.SECOND) TriggleState.NONE else TriggleState.SECOND
copy(
agreeTrigleState = agreeTrigleState,
selectionResult = Pair(agreeNegative, getReactions(this, newTriggle, null))
)
}
} }
} }


fun toggleLike(isFirst: Boolean) = withState {
if (isFirst) {
setState {
val newTriggle = if (it.likeTriggleState == TriggleState.FIRST) TriggleState.NONE else TriggleState.FIRST
copy(
likeTriggleState = newTriggle,
selectionResult = Pair(likePositive, getReactions(this, null, newTriggle))
)
}
} else {
setState {
val newTriggle = if (it.likeTriggleState == TriggleState.SECOND) TriggleState.NONE else TriggleState.SECOND
copy(
likeTriggleState = newTriggle,
selectionResult = Pair(likeNegative, getReactions(this, null, newTriggle))
)
}
}
}

private fun getReactions(state: QuickReactionState, newState1: TriggleState?, newState2: TriggleState?): List<String> {
return ArrayList<String>(4).apply {
when (newState2 ?: state.likeTriggleState) {
TriggleState.FIRST -> add(likePositive)
TriggleState.SECOND -> add(likeNegative)
else -> {
}
}
when (newState1 ?: state.agreeTrigleState) {
TriggleState.FIRST -> add(agreePositive)
TriggleState.SECOND -> add(agreeNegative)
else -> {
}
}
}
}


companion object : MvRxViewModelFactory<QuickReactionViewModel, QuickReactionState> { companion object : MvRxViewModelFactory<QuickReactionViewModel, QuickReactionState> {


val agreePositive = "👍" val quickEmojis = listOf("👍", "👎", "😄", "🎉", "😕", "❤️", "🚀", "👀")
val agreeNegative = "👎"
val likePositive = "🙂"
val likeNegative = "😔"

fun getOpposite(reaction: String): String? {
return when (reaction) {
agreePositive -> agreeNegative
agreeNegative -> agreePositive
likePositive -> likeNegative
likeNegative -> likePositive
else -> null
}
}


override fun initialState(viewModelContext: ViewModelContext): QuickReactionState? { override fun initialState(viewModelContext: ViewModelContext): QuickReactionState? {
// Args are accessible from the context.
// val foo = vieWModelContext.args<MyArgs>.foo
val currentSession = viewModelContext.activity.get<Session>() val currentSession = viewModelContext.activity.get<Session>()
val parcel = viewModelContext.args as TimelineEventFragmentArgs val parcel = viewModelContext.args as TimelineEventFragmentArgs
val event = currentSession.getRoom(parcel.roomId)?.getTimeLineEvent(parcel.eventId) val event = currentSession.getRoom(parcel.roomId)?.getTimeLineEvent(parcel.eventId)
?: return null ?: return null
var agreeTriggle: TriggleState = TriggleState.NONE
var likeTriggle: TriggleState = TriggleState.NONE
event.annotations?.reactionsSummary?.forEach {
//it.addedByMe
if (it.addedByMe) {
if (agreePositive == it.key) {
agreeTriggle = TriggleState.FIRST
} else if (agreeNegative == it.key) {
agreeTriggle = TriggleState.SECOND
}


if (likePositive == it.key) { val summary = event.annotations?.reactionsSummary
likeTriggle = TriggleState.FIRST val quickReactions = quickEmojis.map { emoji ->
} else if (likeNegative == it.key) { ToggleState(emoji, summary?.firstOrNull { it.key == emoji }?.addedByMe ?: false)
likeTriggle = TriggleState.SECOND
}
}
} }
return QuickReactionState(agreeTriggle, likeTriggle, null, event.root.eventId ?: "") return QuickReactionState(quickReactions, event.root.eventId ?: "")
} }
} }
} }

View File

@ -4,116 +4,116 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root_layout" android:id="@+id/root_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="96dp"> android:layout_height="wrap_content"
android:padding="8dp">


<TextView <TextView
android:id="@+id/quick_react_1_text" android:id="@+id/quickReactionTitle"
android:layout_width="40dp" android:layout_width="match_parent"
android:layout_height="40dp" android:layout_height="wrap_content"
android:layout_marginEnd="4dp" android:layout_marginStart="8dp"
android:layout_marginRight="4dp" android:layout_marginEnd="8dp"
android:background="?android:attr/selectableItemBackground" android:text="@string/quick_reactions"
android:clickable="true" android:textColor="?riotx_text_secondary"
android:focusable="true" android:textSize="16sp"
android:textColor="@color/black" app:layout_constraintTop_toTopOf="parent" />
android:textSize="30sp"
app:autoSizeTextType="uniform"
app:layout_constraintBottom_toTopOf="@id/quick_react_agree_text" <TextView
app:layout_constraintEnd_toStartOf="@id/quick_react_2_text" android:id="@+id/quickReaction0"
app:layout_constraintHorizontal_chainStyle="packed" android:layout_width="wrap_content"
app:layout_constraintStart_toStartOf="parent" android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent" android:padding="4dp"
app:layout_constraintVertical_chainStyle="packed" android:textColor="?riotx_text_secondary"
android:textSize="24sp"
tools:ignore="MissingConstraints"
tools:text="👍" /> tools:text="👍" />


<TextView <TextView
android:id="@+id/quick_react_2_text" android:id="@+id/quickReaction1"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginStart="4dp"
android:layout_marginLeft="4dp"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:textColor="@color/black"
android:textSize="30sp"
app:autoSizeTextType="uniform"
app:layout_constraintBottom_toBottomOf="@id/quick_react_1_text"
app:layout_constraintEnd_toStartOf="@id/center_guideline"
app:layout_constraintStart_toEndOf="@id/quick_react_1_text"
app:layout_constraintTop_toTopOf="@id/quick_react_1_text"
tools:text="👎" />


<TextView
android:id="@+id/quick_react_agree_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="@string/reactions_agree"
android:textAlignment="center"
android:textColor="?riotx_text_secondary"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/center_guideline"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/quick_react_1_text" />

<androidx.constraintlayout.widget.Guideline
android:id="@+id/center_guideline"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:padding="4dp"
app:layout_constraintGuide_percent="0.5" /> android:textColor="?riotx_text_secondary"
android:textSize="24sp"
tools:ignore="MissingConstraints"
tools:text="👎" />


<TextView <TextView
android:id="@+id/quick_react_3_text" android:id="@+id/quickReaction2"
android:layout_width="40dp" android:layout_width="wrap_content"
android:layout_height="40dp" android:layout_height="wrap_content"
android:layout_marginEnd="4dp" android:padding="4dp"
android:layout_marginRight="4dp" android:textColor="?riotx_text_secondary"
android:background="?android:attr/selectableItemBackground" android:textSize="24sp"
android:clickable="true" tools:ignore="MissingConstraints"
android:focusable="true"
android:textColor="@color/black"
android:textSize="30sp"
app:autoSizeTextType="uniform"
app:layout_constraintBottom_toBottomOf="@+id/quick_react_1_text"
app:layout_constraintEnd_toStartOf="@id/quick_react_4_text"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toEndOf="@id/center_guideline"
app:layout_constraintTop_toTopOf="@id/quick_react_1_text"
tools:text="😀" /> tools:text="😀" />


<TextView
android:id="@+id/quick_react_4_text"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginStart="4dp"
android:layout_marginLeft="4dp"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:textColor="@color/black"
android:textSize="30sp"
app:autoSizeTextType="uniform"
app:layout_constraintBottom_toBottomOf="@id/quick_react_3_text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/quick_react_3_text"
app:layout_constraintTop_toTopOf="@id/quick_react_3_text"
tools:text="😞" />


<TextView <TextView
android:id="@+id/quick_react_like_text" android:id="@+id/quickReaction3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="4dp"
android:textColor="?riotx_text_secondary"
android:textSize="24sp"
tools:ignore="MissingConstraints"
tools:text="🎉" />

<TextView
android:id="@+id/quickReaction4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="4dp"
android:textColor="?riotx_text_secondary"
android:textSize="24sp"
tools:ignore="MissingConstraints"
tools:text="😕" />

<TextView
android:id="@+id/quickReaction5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="4dp"
android:textColor="?riotx_text_secondary"
android:textSize="24sp"
tools:ignore="MissingConstraints"
tools:text="♥" />

<TextView
android:id="@+id/quickReaction6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="4dp"
android:textColor="?riotx_text_secondary"
android:textSize="24sp"
tools:ignore="MissingConstraints"
tools:text="🍆" />

<TextView
android:id="@+id/quickReaction7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="4dp"
android:textColor="?riotx_text_secondary"
android:textSize="26sp"
tools:ignore="MissingConstraints"
tools:text="👀" />

<androidx.constraintlayout.helper.widget.Flow
android:id="@+id/reactionsFlowHelper"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/reactions_like" android:layout_marginTop="8dp"
android:textAlignment="center" android:layout_marginBottom="16dp"
android:textColor="?riotx_text_secondary" app:constraint_referenced_ids="quickReaction0,quickReaction1,quickReaction2,quickReaction3,quickReaction4,quickReaction5,quickReaction6,quickReaction7"
android:textStyle="bold" app:flow_horizontalGap="8dp"
app:layout_constraintBottom_toBottomOf="@id/quick_react_agree_text" app:flow_horizontalStyle="spread"
app:flow_verticalBias="0"
app:flow_verticalGap="4dp"
app:flow_wrapMode="chain"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/center_guideline" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/quick_react_agree_text" /> app:layout_constraintTop_toBottomOf="@id/quickReactionTitle" />


</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -10,4 +10,5 @@
<string name="settings_other_third_party_notices">Other third party notices</string> <string name="settings_other_third_party_notices">Other third party notices</string>
<string name="navigate_to_room_when_already_in_the_room">You are already viewing this room!</string> <string name="navigate_to_room_when_already_in_the_room">You are already viewing this room!</string>


<string name="quick_reactions">Quick Reactions</string>
</resources> </resources>