Cleaner code: add TimelineEvent to special modes

This commit is contained in:
Benoit Marty 2019-07-10 17:05:32 +02:00
parent 7e8cd07e1e
commit 9a57a02996
3 changed files with 74 additions and 99 deletions

View File

@ -33,6 +33,7 @@ import android.view.View
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.annotation.DrawableRes
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.lifecycle.ViewModelProviders import androidx.lifecycle.ViewModelProviders
@ -225,79 +226,57 @@ class RoomDetailFragment :
} }
} }
roomDetailViewModel.selectSubscribe( roomDetailViewModel.selectSubscribe(RoomDetailViewState::sendMode) { mode ->
RoomDetailViewState::sendMode,
RoomDetailViewState::selectedEvent,
RoomDetailViewState::roomId) { mode, event, roomId ->
when (mode) { when (mode) {
SendMode.REGULAR -> { SendMode.REGULAR -> exitSpecialMode()
commandAutocompletePolicy.enabled = true is SendMode.EDIT -> enterSpecialMode(mode.timelineEvent, R.drawable.ic_edit, true)
val uid = session.sessionParams.credentials.userId is SendMode.QUOTE -> enterSpecialMode(mode.timelineEvent, R.drawable.ic_quote, false)
val meMember = session.getRoom(roomId)?.getRoomMember(uid) is SendMode.REPLY -> enterSpecialMode(mode.timelineEvent, R.drawable.ic_reply, false)
avatarRenderer.render(meMember?.avatarUrl, uid, meMember?.displayName, composerLayout.composerAvatarImageView)
composerLayout.collapse()
}
SendMode.EDIT,
SendMode.QUOTE,
SendMode.REPLY -> {
commandAutocompletePolicy.enabled = false
if (event == null) {
//we should ignore? can this happen?
Timber.e("Enter edit mode with no event selected")
return@selectSubscribe
}
//switch to expanded bar
composerLayout.composerRelatedMessageTitle.apply {
text = event.getDisambiguatedDisplayName()
setTextColor(ContextCompat.getColor(requireContext(), getColorFromUserId(event.root.senderId)))
}
//TODO this is used at several places, find way to refactor?
val messageContent: MessageContent? =
event.annotations?.editSummary?.aggregatedContent?.toModel()
?: event.root.getClearContent().toModel()
val nonFormattedBody = messageContent?.body ?: ""
var formattedBody: CharSequence? = null
if (messageContent is MessageTextContent && messageContent.format == MessageType.FORMAT_MATRIX_HTML) {
val parser = Parser.builder().build()
val document = parser.parse(messageContent.formattedBody
?: messageContent.body)
formattedBody = Markwon.builder(requireContext())
.usePlugin(HtmlPlugin.create()).build().render(document)
}
composerLayout.composerRelatedMessageContent.text = formattedBody
?: nonFormattedBody
if (mode == SendMode.EDIT) {
//TODO if it's a reply we should trim the top part of message
composerLayout.composerEditText.setText(nonFormattedBody)
composerLayout.composerRelatedMessageActionIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_edit))
} else if (mode == SendMode.QUOTE) {
composerLayout.composerEditText.setText("")
composerLayout.composerRelatedMessageActionIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_quote))
} else if (mode == SendMode.REPLY) {
composerLayout.composerEditText.setText("")
composerLayout.composerRelatedMessageActionIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_reply))
}
avatarRenderer.render(event.senderAvatar, event.root.senderId
?: "", event.senderName, composerLayout.composerRelatedMessageAvatar)
composerLayout.composerEditText.setSelection(composerLayout.composerEditText.text.length)
composerLayout.expand {
focusComposerAndShowKeyboard()
}
composerLayout.composerRelatedMessageCloseButton.setOnClickListener {
composerLayout.composerEditText.setText("")
roomDetailViewModel.resetSendMode()
}
}
} }
} }
} }
private fun exitSpecialMode() {
commandAutocompletePolicy.enabled = true
composerLayout.collapse()
}
private fun enterSpecialMode(event: TimelineEvent, @DrawableRes iconRes: Int, useText: Boolean) {
commandAutocompletePolicy.enabled = false
//switch to expanded bar
composerLayout.composerRelatedMessageTitle.apply {
text = event.getDisambiguatedDisplayName()
setTextColor(ContextCompat.getColor(requireContext(), getColorFromUserId(event.root.senderId)))
}
//TODO this is used at several places, find way to refactor?
val messageContent: MessageContent? =
event.annotations?.editSummary?.aggregatedContent?.toModel()
?: event.root.getClearContent().toModel()
val nonFormattedBody = messageContent?.body ?: ""
var formattedBody: CharSequence? = null
if (messageContent is MessageTextContent && messageContent.format == MessageType.FORMAT_MATRIX_HTML) {
val parser = Parser.builder().build()
val document = parser.parse(messageContent.formattedBody
?: messageContent.body)
formattedBody = Markwon.builder(requireContext())
.usePlugin(HtmlPlugin.create()).build().render(document)
}
composerLayout.composerRelatedMessageContent.text = formattedBody
?: nonFormattedBody
composerLayout.composerEditText.setText(if (useText) nonFormattedBody else "")
composerLayout.composerRelatedMessageActionIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), iconRes))
avatarRenderer.render(event.senderAvatar, event.root.senderId
?: "", event.senderName, composerLayout.composerRelatedMessageAvatar)
composerLayout.composerEditText.setSelection(composerLayout.composerEditText.text.length)
composerLayout.expand {
focusComposerAndShowKeyboard()
}
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
@ -422,6 +401,10 @@ class RoomDetailFragment :
roomDetailViewModel.process(RoomDetailActions.SendMessage(textMessage, VectorPreferences.isMarkdownEnabled(requireContext()))) roomDetailViewModel.process(RoomDetailActions.SendMessage(textMessage, VectorPreferences.isMarkdownEnabled(requireContext())))
} }
} }
composerLayout.composerRelatedMessageCloseButton.setOnClickListener {
composerLayout.composerEditText.setText("")
roomDetailViewModel.resetSendMode()
}
} }
private fun setupAttachmentButton() { private fun setupAttachmentButton() {

View File

@ -124,11 +124,10 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
} }
} }
fun enterEditMode(event: TimelineEvent) { private fun enterEditMode(event: TimelineEvent) {
setState { setState {
copy( copy(
sendMode = SendMode.EDIT, sendMode = SendMode.EDIT(event)
selectedEvent = event
) )
} }
} }
@ -136,8 +135,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
fun resetSendMode() { fun resetSendMode() {
setState { setState {
copy( copy(
sendMode = SendMode.REGULAR, sendMode = SendMode.REGULAR
selectedEvent = null
) )
} }
} }
@ -165,7 +163,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
private fun handleSendMessage(action: RoomDetailActions.SendMessage) { private fun handleSendMessage(action: RoomDetailActions.SendMessage) {
withState { state -> withState { state ->
when (state.sendMode) { when (state.sendMode) {
SendMode.REGULAR -> { SendMode.REGULAR -> {
val slashCommandResult = CommandParser.parseSplashCommand(action.text) val slashCommandResult = CommandParser.parseSplashCommand(action.text)
when (slashCommandResult) { when (slashCommandResult) {
@ -231,30 +229,29 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
} }
} }
} }
SendMode.EDIT -> { is SendMode.EDIT -> {
val messageContent: MessageContent? = val messageContent: MessageContent? =
state.selectedEvent?.annotations?.editSummary?.aggregatedContent.toModel() state.sendMode.timelineEvent.annotations?.editSummary?.aggregatedContent.toModel()
?: state.selectedEvent?.root?.getClearContent().toModel() ?: state.sendMode.timelineEvent.root.getClearContent().toModel()
val nonFormattedBody = messageContent?.body ?: "" val nonFormattedBody = messageContent?.body ?: ""
if (nonFormattedBody != action.text) { if (nonFormattedBody != action.text) {
room.editTextMessage(state.selectedEvent?.root?.eventId room.editTextMessage(state.sendMode.timelineEvent.root.eventId
?: "", action.text, action.autoMarkdown) ?: "", action.text, action.autoMarkdown)
} else { } else {
Timber.w("Same message content, do not send edition") Timber.w("Same message content, do not send edition")
} }
setState { setState {
copy( copy(
sendMode = SendMode.REGULAR, sendMode = SendMode.REGULAR
selectedEvent = null
) )
} }
_sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.MessageSent)) _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.MessageSent))
} }
SendMode.QUOTE -> { is SendMode.QUOTE -> {
val messageContent: MessageContent? = val messageContent: MessageContent? =
state.selectedEvent?.annotations?.editSummary?.aggregatedContent.toModel() state.sendMode.timelineEvent.annotations?.editSummary?.aggregatedContent.toModel()
?: state.selectedEvent?.root?.getClearContent().toModel() ?: state.sendMode.timelineEvent.root.getClearContent().toModel()
val textMsg = messageContent?.body val textMsg = messageContent?.body
val finalText = legacyRiotQuoteText(textMsg, action.text) val finalText = legacyRiotQuoteText(textMsg, action.text)
@ -271,19 +268,17 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
} }
setState { setState {
copy( copy(
sendMode = SendMode.REGULAR, sendMode = SendMode.REGULAR
selectedEvent = null
) )
} }
_sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.MessageSent)) _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.MessageSent))
} }
SendMode.REPLY -> { is SendMode.REPLY -> {
state.selectedEvent?.let { state.sendMode.timelineEvent.let {
room.replyToMessage(it.root, action.text, action.autoMarkdown) room.replyToMessage(it.root, action.text, action.autoMarkdown)
setState { setState {
copy( copy(
sendMode = SendMode.REGULAR, sendMode = SendMode.REGULAR
selectedEvent = null
) )
} }
_sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.MessageSent)) _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.MessageSent))
@ -434,8 +429,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
room.getTimeLineEvent(action.eventId)?.let { room.getTimeLineEvent(action.eventId)?.let {
setState { setState {
copy( copy(
sendMode = SendMode.QUOTE, sendMode = SendMode.QUOTE(it)
selectedEvent = it
) )
} }
} }
@ -445,8 +439,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
room.getTimeLineEvent(action.eventId)?.let { room.getTimeLineEvent(action.eventId)?.let {
setState { setState {
copy( copy(
sendMode = SendMode.REPLY, sendMode = SendMode.REPLY(it)
selectedEvent = it
) )
} }
} }

View File

@ -32,11 +32,11 @@ import im.vector.matrix.android.api.session.user.model.User
* *
* Depending on the state the bottom toolbar will change (icons/preview/actions...) * Depending on the state the bottom toolbar will change (icons/preview/actions...)
*/ */
enum class SendMode { sealed class SendMode {
REGULAR, object REGULAR : SendMode()
QUOTE, data class QUOTE(val timelineEvent: TimelineEvent) : SendMode()
EDIT, data class EDIT(val timelineEvent: TimelineEvent) : SendMode()
REPLY data class REPLY(val timelineEvent: TimelineEvent) : SendMode()
} }
data class RoomDetailViewState( data class RoomDetailViewState(
@ -46,7 +46,6 @@ data class RoomDetailViewState(
val asyncInviter: Async<User> = Uninitialized, val asyncInviter: Async<User> = Uninitialized,
val asyncRoomSummary: Async<RoomSummary> = Uninitialized, val asyncRoomSummary: Async<RoomSummary> = Uninitialized,
val sendMode: SendMode = SendMode.REGULAR, val sendMode: SendMode = SendMode.REGULAR,
val selectedEvent: TimelineEvent? = null,
val isEncrypted: Boolean = false val isEncrypted: Boolean = false
) : MvRxState { ) : MvRxState {