mirror of
https://github.com/element-hq/element-android
synced 2024-11-24 02:15:35 +03:00
Update quick reactions to new design
This commit is contained in:
parent
92eb7d55dc
commit
43ead66991
13 changed files with 145 additions and 413 deletions
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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()
|
||||||
object AcceptInvite : RoomDetailActions()
|
object AcceptInvite : RoomDetailActions()
|
||||||
object RejectInvite : RoomDetailActions()
|
object RejectInvite : RoomDetailActions()
|
||||||
|
|
|
@ -681,9 +681,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 -> {
|
||||||
|
|
|
@ -333,7 +333,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) {
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,10 +20,6 @@ import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
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 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 +27,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 +37,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 +47,38 @@ class QuickReactionFragment : BaseMvRxFragment() {
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val textViews by lazy {
|
||||||
|
listOf(quickReaction0, quickReaction1, quickReaction2, quickReaction3,
|
||||||
|
quickReaction4, quickReaction5, quickReaction6, quickReaction7)
|
||||||
|
}
|
||||||
|
|
||||||
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.forEachIndexed { index, textView ->
|
||||||
quickReact2Text.text = QuickReactionViewModel.agreeNegative
|
textView.typeface = fontProvider.typeface ?: Typeface.DEFAULT
|
||||||
quickReact3Text.text = QuickReactionViewModel.likePositive
|
textView.setOnClickListener {
|
||||||
quickReact4Text.text = QuickReactionViewModel.likeNegative
|
viewModel.didSelect(index)
|
||||||
|
}
|
||||||
listOf(quickReact1Text, quickReact2Text, quickReact3Text, quickReact4Text).forEach {
|
|
||||||
it.typeface = fontProvider.typeface ?: Typeface.DEFAULT
|
|
||||||
}
|
|
||||||
|
|
||||||
//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 {
|
||||||
|
|
|
@ -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 ?: "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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>
|
||||||
|
|
|
@ -8,4 +8,5 @@
|
||||||
|
|
||||||
<string name="settings_sdk_version">Matrix SDK Version</string>
|
<string name="settings_sdk_version">Matrix SDK Version</string>
|
||||||
|
|
||||||
|
<string name="quick_reactions">Quick Reactions</string>
|
||||||
</resources>
|
</resources>
|
Loading…
Reference in a new issue