forked from GitHub-Mirror/riotX-android
Design update
+ Reply + Better preview in action menu
This commit is contained in:
@ -60,7 +60,7 @@ class HomeModule {
|
||||
val timelineDateFormatter = TimelineDateFormatter(get())
|
||||
val timelineMediaSizeProvider = TimelineMediaSizeProvider()
|
||||
val colorProvider = ColorProvider(fragment.requireContext())
|
||||
val messageItemFactory = MessageItemFactory(colorProvider, timelineMediaSizeProvider, timelineDateFormatter, eventHtmlRenderer)
|
||||
val messageItemFactory = MessageItemFactory(colorProvider, timelineMediaSizeProvider, timelineDateFormatter, eventHtmlRenderer,get())
|
||||
|
||||
val timelineItemFactory = TimelineItemFactory(messageItemFactory = messageItemFactory,
|
||||
roomNameItemFactory = RoomNameItemFactory(get()),
|
||||
|
@ -38,6 +38,7 @@ sealed class RoomDetailActions {
|
||||
|
||||
data class EnterEditMode(val eventId: String) : RoomDetailActions()
|
||||
data class EnterQuoteMode(val eventId: String) : RoomDetailActions()
|
||||
data class EnterReplyMode(val eventId: String) : RoomDetailActions()
|
||||
|
||||
|
||||
}
|
@ -208,7 +208,8 @@ class RoomDetailFragment :
|
||||
composerLayout.collapse()
|
||||
}
|
||||
SendMode.EDIT,
|
||||
SendMode.QUOTE -> {
|
||||
SendMode.QUOTE,
|
||||
SendMode.REPLY -> {
|
||||
commandAutocompletePolicy.enabled = false
|
||||
if (event == null) {
|
||||
//we should ignore? can this happen?
|
||||
@ -233,9 +234,12 @@ class RoomDetailFragment :
|
||||
if (mode == SendMode.EDIT) {
|
||||
composerLayout.composerEditText.setText(eventTextBody)
|
||||
composerLayout.composerRelatedMessageActionIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_edit))
|
||||
} else {
|
||||
} 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.sender
|
||||
@ -673,13 +677,17 @@ class RoomDetailFragment :
|
||||
}
|
||||
}
|
||||
MessageMenuViewModel.ACTION_EDIT -> {
|
||||
val eventId = actionData.data.toString() ?: return@let
|
||||
val eventId = actionData.data.toString()
|
||||
roomDetailViewModel.process(RoomDetailActions.EnterEditMode(eventId))
|
||||
}
|
||||
MessageMenuViewModel.ACTION_QUOTE -> {
|
||||
val eventId = actionData.data.toString() ?: return@let
|
||||
val eventId = actionData.data.toString()
|
||||
roomDetailViewModel.process(RoomDetailActions.EnterQuoteMode(eventId))
|
||||
}
|
||||
MessageMenuViewModel.ACTION_REPLY -> {
|
||||
val eventId = actionData.data.toString()
|
||||
roomDetailViewModel.process(RoomDetailActions.EnterReplyMode(eventId))
|
||||
}
|
||||
else -> {
|
||||
Toast.makeText(context, "Action ${actionData.actionId} not implemented", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
@ -96,6 +96,7 @@ class RoomDetailViewModel(initialState: RoomDetailViewState,
|
||||
is RoomDetailActions.ShowEditHistoryAction -> handleShowEditHistoryReaction(action)
|
||||
is RoomDetailActions.EnterEditMode -> handleEditAction(action)
|
||||
is RoomDetailActions.EnterQuoteMode -> handleQuoteAction(action)
|
||||
is RoomDetailActions.EnterReplyMode -> handleReplyAction(action)
|
||||
}
|
||||
}
|
||||
|
||||
@ -208,24 +209,34 @@ class RoomDetailViewModel(initialState: RoomDetailViewState,
|
||||
_sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.MessageSent))
|
||||
}
|
||||
SendMode.QUOTE -> {
|
||||
withState { state ->
|
||||
val messageContent: MessageContent? =
|
||||
state.selectedEvent?.annotations?.editSummary?.aggregatedContent?.toModel()
|
||||
?: state.selectedEvent?.root?.content.toModel()
|
||||
val textMsg = messageContent?.body
|
||||
val messageContent: MessageContent? =
|
||||
state.selectedEvent?.annotations?.editSummary?.aggregatedContent?.toModel()
|
||||
?: state.selectedEvent?.root?.content.toModel()
|
||||
val textMsg = messageContent?.body
|
||||
|
||||
val finalText = legacyRiotQuoteText(textMsg, action.text)
|
||||
val finalText = legacyRiotQuoteText(textMsg, action.text)
|
||||
|
||||
//TODO Refactor this, just temporary for quotes
|
||||
val parser = Parser.builder().build()
|
||||
val document = parser.parse(finalText)
|
||||
val renderer = HtmlRenderer.builder().build()
|
||||
val htmlText = renderer.render(document)
|
||||
if (TextUtils.equals(finalText, htmlText)) {
|
||||
room.sendTextMessage(finalText)
|
||||
} else {
|
||||
room.sendFormattedTextMessage(finalText, htmlText)
|
||||
}
|
||||
//TODO Refactor this, just temporary for quotes
|
||||
val parser = Parser.builder().build()
|
||||
val document = parser.parse(finalText)
|
||||
val renderer = HtmlRenderer.builder().build()
|
||||
val htmlText = renderer.render(document)
|
||||
if (TextUtils.equals(finalText, htmlText)) {
|
||||
room.sendTextMessage(finalText)
|
||||
} else {
|
||||
room.sendFormattedTextMessage(finalText, htmlText)
|
||||
}
|
||||
setState {
|
||||
copy(
|
||||
sendMode = SendMode.REGULAR,
|
||||
selectedEvent = null
|
||||
)
|
||||
}
|
||||
_sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.MessageSent))
|
||||
}
|
||||
SendMode.REPLY -> {
|
||||
state.selectedEvent?.let {
|
||||
room.replyToMessage(it.root, action.text)
|
||||
setState {
|
||||
copy(
|
||||
sendMode = SendMode.REGULAR,
|
||||
@ -377,6 +388,17 @@ class RoomDetailViewModel(initialState: RoomDetailViewState,
|
||||
}
|
||||
}
|
||||
}
|
||||
private fun handleReplyAction(action: RoomDetailActions.EnterReplyMode) {
|
||||
room.getTimeLineEvent(action.eventId)?.let {
|
||||
setState {
|
||||
copy(
|
||||
sendMode = SendMode.REPLY,
|
||||
selectedEvent = it
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private fun observeEventDisplayedActions() {
|
||||
|
@ -36,7 +36,8 @@ import im.vector.matrix.android.api.session.user.model.User
|
||||
enum class SendMode {
|
||||
REGULAR,
|
||||
QUOTE,
|
||||
EDIT
|
||||
EDIT,
|
||||
REPLY
|
||||
}
|
||||
|
||||
data class RoomDetailViewState(
|
||||
|
@ -21,8 +21,14 @@ import com.airbnb.mvrx.ViewModelContext
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageTextContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageType
|
||||
import im.vector.riotredesign.core.platform.VectorViewModel
|
||||
import org.commonmark.parser.Parser
|
||||
import org.commonmark.renderer.html.HtmlRenderer
|
||||
import org.koin.android.ext.android.get
|
||||
import ru.noties.markwon.Markwon
|
||||
import ru.noties.markwon.html.HtmlPlugin
|
||||
import timber.log.Timber
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
@ -31,7 +37,7 @@ import java.util.*
|
||||
data class MessageActionState(
|
||||
val userId: String,
|
||||
val senderName: String,
|
||||
val messageBody: String,
|
||||
val messageBody: CharSequence,
|
||||
val ts: String?,
|
||||
val senderAvatarPath: String? = null)
|
||||
: MvRxState
|
||||
@ -54,10 +60,19 @@ class MessageActionsViewModel(initialState: MessageActionState) : VectorViewMode
|
||||
val messageContent: MessageContent? = event.annotations?.editSummary?.aggregatedContent?.toModel()
|
||||
?: event.root.content.toModel()
|
||||
val originTs = event.root.originServerTs
|
||||
var body: CharSequence = messageContent?.body ?: ""
|
||||
if (messageContent is MessageTextContent && messageContent.format == MessageType.FORMAT_MATRIX_HTML) {
|
||||
val parser = Parser.builder().build()
|
||||
val document = parser.parse(messageContent.formattedBody ?: messageContent.body)
|
||||
// val renderer = HtmlRenderer.builder().build()
|
||||
body = Markwon.builder(viewModelContext.activity)
|
||||
.usePlugin(HtmlPlugin.create()).build().render(document)
|
||||
// body = renderer.render(document)
|
||||
}
|
||||
MessageActionState(
|
||||
event.root.sender ?: "",
|
||||
parcel.informationData.memberName.toString(),
|
||||
messageContent?.body ?: "",
|
||||
body,
|
||||
dateFormat.format(Date(originTs ?: 0)),
|
||||
currentSession.contentUrlResolver().resolveFullSize(parcel.informationData.avatarUrl)
|
||||
)
|
||||
|
@ -57,7 +57,7 @@ class MessageMenuViewModel(initialState: MessageMenuState) : VectorViewModel<Mes
|
||||
//Resend and Delete
|
||||
return MessageMenuState(
|
||||
listOf(
|
||||
SimpleAction(ACTION_RESEND, R.string.resend, R.drawable.ic_corner_down_right, event.root.eventId),
|
||||
SimpleAction(ACTION_RESEND, R.string.resend, R.drawable.ic_send, event.root.eventId),
|
||||
//TODO delete icon
|
||||
SimpleAction(ACTION_DELETE, R.string.delete, R.drawable.ic_delete, event.root.eventId)
|
||||
)
|
||||
@ -80,6 +80,10 @@ class MessageMenuViewModel(initialState: MessageMenuState) : VectorViewModel<Mes
|
||||
this.add(SimpleAction(ACTION_COPY, R.string.copy, R.drawable.ic_copy, messageContent.body))
|
||||
}
|
||||
|
||||
if (canReply(event, messageContent)) {
|
||||
this.add(SimpleAction(ACTION_REPLY, R.string.reply, R.drawable.ic_reply, event.root.eventId))
|
||||
}
|
||||
|
||||
if (canEdit(event, currentSession.sessionParams.credentials.userId)) {
|
||||
this.add(SimpleAction(ACTION_EDIT, R.string.edit, R.drawable.ic_edit, event.root.eventId))
|
||||
}
|
||||
@ -93,9 +97,6 @@ class MessageMenuViewModel(initialState: MessageMenuState) : VectorViewModel<Mes
|
||||
this.add(SimpleAction(ACTION_QUOTE, R.string.quote, R.drawable.ic_quote, parcel.eventId))
|
||||
}
|
||||
|
||||
if (canReply(event, messageContent)) {
|
||||
this.add(SimpleAction(ACTION_REPLY, R.string.reply, R.drawable.ic_corner_down_right))
|
||||
}
|
||||
if (canShare(type)) {
|
||||
if (messageContent is MessageImageContent) {
|
||||
this.add(
|
||||
|
@ -38,6 +38,7 @@ import im.vector.riotredesign.core.epoxy.VectorEpoxyModel
|
||||
import im.vector.riotredesign.core.extensions.localDateTime
|
||||
import im.vector.riotredesign.core.linkify.VectorLinkify
|
||||
import im.vector.riotredesign.core.resources.ColorProvider
|
||||
import im.vector.riotredesign.core.resources.StringProvider
|
||||
import im.vector.riotredesign.core.utils.DebouncedClickListener
|
||||
import im.vector.riotredesign.features.home.AvatarRenderer
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController
|
||||
@ -52,7 +53,8 @@ import me.gujun.android.span.span
|
||||
class MessageItemFactory(private val colorProvider: ColorProvider,
|
||||
private val timelineMediaSizeProvider: TimelineMediaSizeProvider,
|
||||
private val timelineDateFormatter: TimelineDateFormatter,
|
||||
private val htmlRenderer: EventHtmlRenderer) {
|
||||
private val htmlRenderer: EventHtmlRenderer,
|
||||
private val stringProvider: StringProvider) {
|
||||
|
||||
fun create(event: TimelineEvent,
|
||||
nextEvent: TimelineEvent?,
|
||||
@ -100,8 +102,10 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
|
||||
val messageContent: MessageContent =
|
||||
event.annotations?.editSummary?.aggregatedContent?.toModel()
|
||||
?: event.root.content.toModel()
|
||||
?: return null
|
||||
?: //Malformed content, we should echo something on screen
|
||||
return DefaultItem_().text(stringProvider.getString(R.string.malformed_message))
|
||||
|
||||
//TODO this should be filtered as not displayable?
|
||||
if (messageContent.relatesTo?.type == RelationType.REPLACE) {
|
||||
//TODO blank item or ignore??
|
||||
// ignore this event
|
||||
|
@ -1,22 +1,22 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="22dp"
|
||||
android:height="22dp"
|
||||
android:height="13dp"
|
||||
android:viewportWidth="22"
|
||||
android:viewportHeight="22">
|
||||
android:viewportHeight="13">
|
||||
<path
|
||||
android:pathData="M14.75,8.5L21,14.75 14.75,21"
|
||||
android:pathData="M5.4444,1l-4.4444,4.3636l4.4444,4.3636"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:fillType="evenOdd"
|
||||
android:strokeColor="#9E9E9E"
|
||||
android:fillType="evenOdd"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M1,1v8.75a5,5 0,0 0,5 5h15"
|
||||
android:pathData="M21,11.9091L21,9.7273C21,7.3173 19.0102,5.3636 16.5556,5.3636L1,5.3636"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:fillType="evenOdd"
|
||||
android:strokeColor="#9E9E9E"
|
||||
android:fillType="evenOdd"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
@ -17,4 +17,7 @@
|
||||
<string name="event_redacted_by_admin_reason">Event moderated by room admin</string>
|
||||
<string name="last_edited_info_message">Last edited by %s on %s</string>
|
||||
|
||||
|
||||
<string name="malformed_message">Malformed event, cannot display</string>
|
||||
|
||||
</resources>
|
Reference in New Issue
Block a user