forked from GitHub-Mirror/riotX-android
Toggle Quick React (agree/disagree like/dislike)
This commit is contained in:
@ -30,6 +30,7 @@ sealed class RoomDetailActions {
|
||||
data class SendReaction(val reaction: String, val targetEventId: 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 UpdateQuickReactAction(val targetEventId: String,val selectedReaction: String,val opposite: String) : RoomDetailActions()
|
||||
object AcceptInvite : RoomDetailActions()
|
||||
object RejectInvite : RoomDetailActions()
|
||||
|
||||
|
@ -54,11 +54,7 @@ import com.otaliastudios.autocomplete.AutocompleteCallback
|
||||
import com.otaliastudios.autocomplete.CharPolicy
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.session.room.model.Membership
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageAudioContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageFileContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageImageContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageVideoContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.*
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||
import im.vector.matrix.android.api.session.user.model.User
|
||||
import im.vector.riotredesign.R
|
||||
@ -69,15 +65,7 @@ import im.vector.riotredesign.core.extensions.observeEvent
|
||||
import im.vector.riotredesign.core.glide.GlideApp
|
||||
import im.vector.riotredesign.core.platform.ToolbarConfigurable
|
||||
import im.vector.riotredesign.core.platform.VectorBaseFragment
|
||||
import im.vector.riotredesign.core.utils.LiveEvent
|
||||
import im.vector.riotredesign.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
|
||||
import im.vector.riotredesign.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_CAMERA
|
||||
import im.vector.riotredesign.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_NATIVE_CAMERA
|
||||
import im.vector.riotredesign.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_NATIVE_VIDEO_CAMERA
|
||||
import im.vector.riotredesign.core.utils.checkPermissions
|
||||
import im.vector.riotredesign.core.utils.copyToClipboard
|
||||
import im.vector.riotredesign.core.utils.openCamera
|
||||
import im.vector.riotredesign.core.utils.shareMedia
|
||||
import im.vector.riotredesign.core.utils.*
|
||||
import im.vector.riotredesign.features.autocomplete.command.AutocompleteCommandPresenter
|
||||
import im.vector.riotredesign.features.autocomplete.command.CommandAutocompletePolicy
|
||||
import im.vector.riotredesign.features.autocomplete.user.AutocompleteUserPresenter
|
||||
@ -199,11 +187,11 @@ class RoomDetailFragment :
|
||||
if (resultCode == RESULT_OK && data != null) {
|
||||
when (requestCode) {
|
||||
REQUEST_FILES_REQUEST_CODE, TAKE_IMAGE_REQUEST_CODE -> handleMediaIntent(data)
|
||||
REACTION_SELECT_REQUEST_CODE -> {
|
||||
REACTION_SELECT_REQUEST_CODE -> {
|
||||
val eventId = data.getStringExtra(EmojiReactionPickerActivity.EXTRA_EVENT_ID)
|
||||
?: return
|
||||
?: return
|
||||
val reaction = data.getStringExtra(EmojiReactionPickerActivity.EXTRA_REACTION_RESULT)
|
||||
?: return
|
||||
?: return
|
||||
//TODO check if already reacted with that?
|
||||
roomDetailViewModel.process(RoomDetailActions.SendReaction(reaction, eventId))
|
||||
}
|
||||
@ -367,24 +355,24 @@ class RoomDetailFragment :
|
||||
private fun onSendChoiceClicked(dialogListItem: DialogListItem) {
|
||||
Timber.v("On send choice clicked: $dialogListItem")
|
||||
when (dialogListItem) {
|
||||
is DialogListItem.SendFile -> {
|
||||
is DialogListItem.SendFile -> {
|
||||
// launchFileIntent
|
||||
}
|
||||
is DialogListItem.SendVoice -> {
|
||||
is DialogListItem.SendVoice -> {
|
||||
//launchAudioRecorderIntent()
|
||||
}
|
||||
is DialogListItem.SendSticker -> {
|
||||
is DialogListItem.SendSticker -> {
|
||||
//startStickerPickerActivity()
|
||||
}
|
||||
is DialogListItem.TakePhotoVideo ->
|
||||
if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, requireActivity(), PERMISSION_REQUEST_CODE_LAUNCH_CAMERA)) {
|
||||
// launchCamera()
|
||||
}
|
||||
is DialogListItem.TakePhoto ->
|
||||
is DialogListItem.TakePhoto ->
|
||||
if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, requireActivity(), PERMISSION_REQUEST_CODE_LAUNCH_NATIVE_CAMERA)) {
|
||||
openCamera(requireActivity(), CAMERA_VALUE_TITLE, TAKE_IMAGE_REQUEST_CODE)
|
||||
}
|
||||
is DialogListItem.TakeVideo ->
|
||||
is DialogListItem.TakeVideo ->
|
||||
if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, requireActivity(), PERMISSION_REQUEST_CODE_LAUNCH_NATIVE_VIDEO_CAMERA)) {
|
||||
// launchNativeVideoRecorder()
|
||||
}
|
||||
@ -431,20 +419,20 @@ class RoomDetailFragment :
|
||||
private fun renderSendMessageResult(sendMessageResult: SendMessageResult) {
|
||||
when (sendMessageResult) {
|
||||
is SendMessageResult.MessageSent,
|
||||
is SendMessageResult.SlashCommandHandled -> {
|
||||
is SendMessageResult.SlashCommandHandled -> {
|
||||
// Clear composer
|
||||
composerEditText.text = null
|
||||
}
|
||||
is SendMessageResult.SlashCommandError -> {
|
||||
is SendMessageResult.SlashCommandError -> {
|
||||
displayCommandError(getString(R.string.command_problem_with_parameters, sendMessageResult.command.command))
|
||||
}
|
||||
is SendMessageResult.SlashCommandUnknown -> {
|
||||
is SendMessageResult.SlashCommandUnknown -> {
|
||||
displayCommandError(getString(R.string.unrecognized_command, sendMessageResult.command))
|
||||
}
|
||||
is SendMessageResult.SlashCommandResultOk -> {
|
||||
is SendMessageResult.SlashCommandResultOk -> {
|
||||
// Ignore
|
||||
}
|
||||
is SendMessageResult.SlashCommandResultError -> {
|
||||
is SendMessageResult.SlashCommandResultError -> {
|
||||
displayCommandError(sendMessageResult.throwable.localizedMessage)
|
||||
}
|
||||
is SendMessageResult.SlashCommandNotImplemented -> {
|
||||
@ -522,7 +510,7 @@ class RoomDetailFragment :
|
||||
roomDetailViewModel.process(RoomDetailActions.SendReaction(reaction, informationData.eventId))
|
||||
} else {
|
||||
//I need to redact a reaction
|
||||
roomDetailViewModel.process(RoomDetailActions.UndoReaction(informationData.eventId,reaction))
|
||||
roomDetailViewModel.process(RoomDetailActions.UndoReaction(informationData.eventId, reaction))
|
||||
}
|
||||
}
|
||||
|
||||
@ -536,11 +524,11 @@ class RoomDetailFragment :
|
||||
it?.getContentIfNotHandled()?.let { actionData ->
|
||||
|
||||
when (actionData.actionId) {
|
||||
MessageMenuViewModel.ACTION_ADD_REACTION -> {
|
||||
MessageMenuViewModel.ACTION_ADD_REACTION -> {
|
||||
val eventId = actionData.data?.toString() ?: return
|
||||
startActivityForResult(EmojiReactionPickerActivity.intent(requireContext(), eventId), REACTION_SELECT_REQUEST_CODE)
|
||||
}
|
||||
MessageMenuViewModel.ACTION_COPY -> {
|
||||
MessageMenuViewModel.ACTION_COPY -> {
|
||||
//I need info about the current selected message :/
|
||||
copyToClipboard(requireContext(), actionData.data?.toString() ?: "", false)
|
||||
val snack = Snackbar.make(view!!, requireContext().getString(R.string.copied_to_clipboard), Snackbar.LENGTH_SHORT)
|
||||
@ -549,7 +537,7 @@ class RoomDetailFragment :
|
||||
}
|
||||
MessageMenuViewModel.ACTION_DELETE -> {
|
||||
val eventId = actionData.data?.toString() ?: return
|
||||
roomDetailViewModel.process(RoomDetailActions.RedactAction(eventId,context?.getString(R.string.event_redacted_by_user_reason)))
|
||||
roomDetailViewModel.process(RoomDetailActions.RedactAction(eventId, context?.getString(R.string.event_redacted_by_user_reason)))
|
||||
}
|
||||
MessageMenuViewModel.ACTION_SHARE -> {
|
||||
//TODO current data communication is too limited
|
||||
@ -594,12 +582,13 @@ class RoomDetailFragment :
|
||||
.setPositiveButton(R.string.ok) { dialog, id -> dialog.cancel() }
|
||||
.show()
|
||||
}
|
||||
MessageMenuViewModel.ACTION_QUICK_REACT -> {
|
||||
(actionData.data as? Pair<String, String>)?.let { pairData ->
|
||||
roomDetailViewModel.process(RoomDetailActions.SendReaction(pairData.second, pairData.first))
|
||||
MessageMenuViewModel.ACTION_QUICK_REACT -> {
|
||||
//eventId,ClickedOn,Opposite
|
||||
(actionData.data as? Triple<String, String, String>)?.let { (eventId, clickedOn, opposite) ->
|
||||
roomDetailViewModel.process(RoomDetailActions.UpdateQuickReactAction(eventId, clickedOn, opposite))
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
else -> {
|
||||
Toast.makeText(context, "Action ${actionData.actionId} not implemented", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
|
@ -72,18 +72,21 @@ class RoomDetailViewModel(initialState: RoomDetailViewState,
|
||||
|
||||
fun process(action: RoomDetailActions) {
|
||||
when (action) {
|
||||
is RoomDetailActions.SendMessage -> handleSendMessage(action)
|
||||
is RoomDetailActions.IsDisplayed -> handleIsDisplayed()
|
||||
is RoomDetailActions.SendMedia -> handleSendMedia(action)
|
||||
is RoomDetailActions.SendMessage -> handleSendMessage(action)
|
||||
is RoomDetailActions.IsDisplayed -> handleIsDisplayed()
|
||||
is RoomDetailActions.SendMedia -> handleSendMedia(action)
|
||||
is RoomDetailActions.EventDisplayed -> handleEventDisplayed(action)
|
||||
is RoomDetailActions.LoadMore -> handleLoadMore(action)
|
||||
is RoomDetailActions.SendReaction -> handleSendReaction(action)
|
||||
is RoomDetailActions.AcceptInvite -> handleAcceptInvite()
|
||||
is RoomDetailActions.RejectInvite -> handleRejectInvite()
|
||||
is RoomDetailActions.LoadMore -> handleLoadMore(action)
|
||||
is RoomDetailActions.SendReaction -> handleSendReaction(action)
|
||||
is RoomDetailActions.AcceptInvite -> handleAcceptInvite()
|
||||
is RoomDetailActions.RejectInvite -> handleRejectInvite()
|
||||
is RoomDetailActions.RedactAction -> handleRedactEvent(action)
|
||||
is RoomDetailActions.UndoReaction -> handleUndoReact(action)
|
||||
is RoomDetailActions.UpdateQuickReactAction -> handleUpdateQuickReaction(action)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private val _sendMessageResultLiveData = MutableLiveData<LiveEvent<SendMessageResult>>()
|
||||
val sendMessageResultLiveData: LiveData<LiveEvent<SendMessageResult>>
|
||||
get() = _sendMessageResultLiveData
|
||||
@ -95,63 +98,63 @@ class RoomDetailViewModel(initialState: RoomDetailViewState,
|
||||
val slashCommandResult = CommandParser.parseSplashCommand(action.text)
|
||||
|
||||
when (slashCommandResult) {
|
||||
is ParsedCommand.ErrorNotACommand -> {
|
||||
is ParsedCommand.ErrorNotACommand -> {
|
||||
// Send the text message to the room
|
||||
room.sendTextMessage(action.text)
|
||||
_sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.MessageSent))
|
||||
}
|
||||
is ParsedCommand.ErrorSyntax -> {
|
||||
is ParsedCommand.ErrorSyntax -> {
|
||||
_sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandError(slashCommandResult.command)))
|
||||
}
|
||||
is ParsedCommand.ErrorEmptySlashCommand -> {
|
||||
is ParsedCommand.ErrorEmptySlashCommand -> {
|
||||
_sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandUnknown("/")))
|
||||
}
|
||||
is ParsedCommand.ErrorUnknownSlashCommand -> {
|
||||
_sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandUnknown(slashCommandResult.slashCommand)))
|
||||
}
|
||||
is ParsedCommand.Invite -> {
|
||||
is ParsedCommand.Invite -> {
|
||||
handleInviteSlashCommand(slashCommandResult)
|
||||
}
|
||||
is ParsedCommand.SetUserPowerLevel -> {
|
||||
is ParsedCommand.SetUserPowerLevel -> {
|
||||
// TODO
|
||||
_sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented))
|
||||
}
|
||||
is ParsedCommand.ClearScalarToken -> {
|
||||
is ParsedCommand.ClearScalarToken -> {
|
||||
// TODO
|
||||
_sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented))
|
||||
}
|
||||
is ParsedCommand.SetMarkdown -> {
|
||||
is ParsedCommand.SetMarkdown -> {
|
||||
// TODO
|
||||
_sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented))
|
||||
}
|
||||
is ParsedCommand.UnbanUser -> {
|
||||
is ParsedCommand.UnbanUser -> {
|
||||
// TODO
|
||||
_sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented))
|
||||
}
|
||||
is ParsedCommand.BanUser -> {
|
||||
is ParsedCommand.BanUser -> {
|
||||
// TODO
|
||||
_sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented))
|
||||
}
|
||||
is ParsedCommand.KickUser -> {
|
||||
is ParsedCommand.KickUser -> {
|
||||
// TODO
|
||||
_sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented))
|
||||
}
|
||||
is ParsedCommand.JoinRoom -> {
|
||||
is ParsedCommand.JoinRoom -> {
|
||||
// TODO
|
||||
_sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented))
|
||||
}
|
||||
is ParsedCommand.PartRoom -> {
|
||||
is ParsedCommand.PartRoom -> {
|
||||
// TODO
|
||||
_sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented))
|
||||
}
|
||||
is ParsedCommand.SendEmote -> {
|
||||
is ParsedCommand.SendEmote -> {
|
||||
room.sendTextMessage(slashCommandResult.message, msgType = MessageType.MSGTYPE_EMOTE)
|
||||
_sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandHandled))
|
||||
}
|
||||
is ParsedCommand.ChangeTopic -> {
|
||||
is ParsedCommand.ChangeTopic -> {
|
||||
handleChangeTopicSlashCommand(slashCommandResult)
|
||||
}
|
||||
is ParsedCommand.ChangeDisplayName -> {
|
||||
is ParsedCommand.ChangeDisplayName -> {
|
||||
// TODO
|
||||
_sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented))
|
||||
}
|
||||
@ -201,6 +204,11 @@ class RoomDetailViewModel(initialState: RoomDetailViewState,
|
||||
}
|
||||
|
||||
|
||||
private fun handleUpdateQuickReaction(action: RoomDetailActions.UpdateQuickReactAction) {
|
||||
room.updateQuickReaction(action.selectedReaction, action.opposite, action.targetEventId, session.sessionParams.credentials.userId)
|
||||
}
|
||||
|
||||
|
||||
private fun handleSendMedia(action: RoomDetailActions.SendMedia) {
|
||||
val attachments = action.mediaFiles.map {
|
||||
ContentAttachmentData(
|
||||
@ -238,6 +246,7 @@ class RoomDetailViewModel(initialState: RoomDetailViewState,
|
||||
room.join(object : MatrixCallback<Unit> {})
|
||||
}
|
||||
|
||||
|
||||
private fun observeEventDisplayedActions() {
|
||||
// We are buffering scroll events for one second
|
||||
// and keep the most recent one to set the read receipt on.
|
||||
|
@ -95,13 +95,8 @@ class MessageActionsBottomSheet : BaseMvRxBottomSheetDialog() {
|
||||
.commit()
|
||||
}
|
||||
quickReactionFragment.interactionListener = object : QuickReactionFragment.InteractionListener {
|
||||
override fun didQuickReactWith(clikedOn: String, reactions: List<String>, eventId: String) {
|
||||
if (reactions.contains(clikedOn)) {
|
||||
//it's an add
|
||||
actionHandlerModel.fireAction(MessageMenuViewModel.ACTION_QUICK_REACT, Pair(eventId,clikedOn))
|
||||
} else {
|
||||
//it's a remove
|
||||
}
|
||||
override fun didQuickReactWith(clikedOn: String, opposite: String, reactions: List<String>, eventId: String) {
|
||||
actionHandlerModel.fireAction(MessageMenuViewModel.ACTION_QUICK_REACT, Triple(eventId, clikedOn, opposite))
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
@ -119,12 +119,14 @@ class QuickReactionFragment : BaseMvRxFragment() {
|
||||
}
|
||||
|
||||
if (it.selectionResult != null) {
|
||||
interactionListener?.didQuickReactWith(it.selectionResult.first, it.selectionResult.second, it.eventId)
|
||||
val clikedOn = it.selectionResult.first
|
||||
interactionListener?.didQuickReactWith(clikedOn, QuickReactionViewModel.getOpposite(clikedOn)
|
||||
?: "", it.selectionResult.second, it.eventId)
|
||||
}
|
||||
}
|
||||
|
||||
interface InteractionListener {
|
||||
fun didQuickReactWith(clikedOn: String, reactions: List<String>, eventId: String)
|
||||
fun didQuickReactWith(clikedOn: String, opposite: String, reactions: List<String>, eventId: String)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -110,6 +110,16 @@ class QuickReactionViewModel(initialState: QuickReactionState) : VectorViewModel
|
||||
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? {
|
||||
// Args are accessible from the context.
|
||||
// val foo = vieWModelContext.args<MyArgs>.foo
|
||||
|
@ -125,7 +125,7 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : BaseEventItem<H>() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 && !holder.view.isInLayout) {
|
||||
holder.reactionFlowHelper?.requestLayout()
|
||||
}
|
||||
|
||||
holder.reactionWrapper?.setOnLongClickListener(longClickListener)
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user