Design update

+ Reply 
+ Better preview in action menu
This commit is contained in:
Valere
2019-05-27 11:55:20 +02:00
parent b45cc0e63f
commit 0e06908a48
26 changed files with 350 additions and 59 deletions

View File

@ -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()),

View File

@ -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()
}

View File

@ -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()
}

View File

@ -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() {

View File

@ -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(

View File

@ -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)
)

View File

@ -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(

View File

@ -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

View File

@ -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>

View File

@ -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>