Merge pull request #466 from vector-im/feature/edit_history_item

Add "View Edit History" item in the message bottom sheet (#401)
This commit is contained in:
Benoit Marty 2019-08-07 15:08:26 +02:00 committed by GitHub
commit 80e2fc0ca3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 79 additions and 23 deletions

View File

@ -10,6 +10,7 @@ Improvements:
- Basic support for resending failed messages (retry/remove)
- Enable proper cancellation of suspending functions (including db transaction)
- Enhances network connectivity checks in SDK
- Add "View Edit History" item in the message bottom sheet (#401)

Other changes:
-

View File

@ -84,6 +84,12 @@ data class TimelineEvent(
}
}


/**
* Tells if the event has been edited
*/
fun TimelineEvent.hasBeenEdited() = annotations?.editSummary != null

/**
* Get last MessageContent, after a possible edition
*/
@ -100,4 +106,4 @@ fun TimelineEvent.getTextEditableContent(): String? {
} else {
lastContent?.body ?: ""
}
}
}

View File

@ -59,7 +59,6 @@ import com.otaliastudios.autocomplete.CharPolicy
import im.vector.matrix.android.api.permalinks.PermalinkFactory
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.room.model.EditAggregatedSummary
import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.api.session.room.model.message.*
import im.vector.matrix.android.api.session.room.send.SendState
@ -803,7 +802,7 @@ class RoomDetailFragment :
.show(requireActivity().supportFragmentManager, "DISPLAY_REACTIONS")
}

override fun onEditedDecorationClicked(informationData: MessageInformationData, editAggregatedSummary: EditAggregatedSummary?) {
override fun onEditedDecorationClicked(informationData: MessageInformationData) {
ViewEditHistoryBottomSheet.newInstance(roomDetailArgs.roomId, informationData)
.show(requireActivity().supportFragmentManager, "DISPLAY_EDITS")
}
@ -869,6 +868,9 @@ class RoomDetailFragment :
}
)
}
is SimpleAction.ViewEditHistory -> {
onEditedDecorationClicked(action.messageInformationData)
}
is SimpleAction.ViewSource -> {
val view = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_event_content, null)
view.findViewById<TextView>(R.id.event_content_text_view)?.let {

View File

@ -24,7 +24,6 @@ import androidx.recyclerview.widget.ListUpdateCallback
import androidx.recyclerview.widget.RecyclerView
import com.airbnb.epoxy.EpoxyController
import com.airbnb.epoxy.EpoxyModel
import im.vector.matrix.android.api.session.room.model.EditAggregatedSummary
import im.vector.matrix.android.api.session.room.model.message.*
import im.vector.matrix.android.api.session.room.timeline.Timeline
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
@ -60,7 +59,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Tim
fun onVideoMessageClicked(messageVideoContent: MessageVideoContent, mediaData: VideoContentRenderer.Data, view: View)
fun onFileMessageClicked(eventId: String, messageFileContent: MessageFileContent)
fun onAudioMessageClicked(messageAudioContent: MessageAudioContent)
fun onEditedDecorationClicked(informationData: MessageInformationData, editAggregatedSummary: EditAggregatedSummary?)
fun onEditedDecorationClicked(informationData: MessageInformationData)
}

interface ReactionPillCallback {
@ -159,7 +158,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Tim
synchronized(modelCache) {
for (i in 0 until modelCache.size) {
if (modelCache[i]?.eventId == eventIdToHighlight
|| modelCache[i]?.eventId == this.eventIdToHighlight) {
|| modelCache[i]?.eventId == this.eventIdToHighlight) {
modelCache[i] = null
}
}
@ -220,8 +219,8 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Tim
// Should be build if not cached or if cached but contains mergedHeader or formattedDay
// We then are sure we always have items up to date.
if (modelCache[position] == null
|| modelCache[position]?.mergedHeaderModel != null
|| modelCache[position]?.formattedDayModel != null) {
|| modelCache[position]?.mergedHeaderModel != null
|| modelCache[position]?.formattedDayModel != null) {
modelCache[position] = buildItemModels(position, currentSnapshot)
}
}
@ -294,8 +293,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Tim
// We try to find if one of the item id were used as mergeItemCollapseStates key
// => handle case where paginating from mergeable events and we get more
val previousCollapseStateKey = mergedEventIds.intersect(mergeItemCollapseStates.keys).firstOrNull()
val initialCollapseState = mergeItemCollapseStates.remove(previousCollapseStateKey)
?: true
val initialCollapseState = mergeItemCollapseStates.remove(previousCollapseStateKey) ?: true
val isCollapsed = mergeItemCollapseStates.getOrPut(event.localId) { initialCollapseState }
if (isCollapsed) {
collapsedEventIds.addAll(mergedEventIds)

View File

@ -29,6 +29,7 @@ import im.vector.matrix.android.api.session.room.model.message.MessageImageConte
import im.vector.matrix.android.api.session.room.model.message.MessageType
import im.vector.matrix.android.api.session.room.send.SendState
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.api.session.room.timeline.hasBeenEdited
import im.vector.matrix.rx.RxRoom
import im.vector.riotx.R
import im.vector.riotx.core.extensions.canReact
@ -55,6 +56,8 @@ sealed class SimpleAction(@StringRes val titleRes: Int, @DrawableRes val iconRes
data class Flag(val eventId: String) : SimpleAction(R.string.report_content, R.drawable.ic_flag)
data class QuickReact(val eventId: String, val clickedOn: String, val add: Boolean) : SimpleAction(0, 0)
data class ViewReactions(val messageInformationData: MessageInformationData) : SimpleAction(R.string.message_view_reaction, R.drawable.ic_view_reactions)
data class ViewEditHistory(val messageInformationData: MessageInformationData) :
SimpleAction(R.string.message_view_edit_history, R.drawable.ic_view_edit_history)
}

data class MessageMenuState(
@ -155,6 +158,10 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M
add(SimpleAction.ViewReactions(informationData))
}

if (event.hasBeenEdited()) {
add(SimpleAction.ViewEditHistory(informationData))
}

if (canShare(type)) {
if (messageContent is MessageImageContent) {
add(SimpleAction.Share(session.contentUrlResolver().resolveFullSize(messageContent.url)))

View File

@ -28,7 +28,6 @@ import im.vector.matrix.android.api.permalinks.MatrixLinkify
import im.vector.matrix.android.api.permalinks.MatrixPermalinkSpan
import im.vector.matrix.android.api.session.events.model.RelationType
import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.model.EditAggregatedSummary
import im.vector.matrix.android.api.session.room.model.message.*
import im.vector.matrix.android.api.session.room.send.SendState
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
@ -118,13 +117,11 @@ class MessageItemFactory @Inject constructor(
return when (messageContent) {
is MessageEmoteContent -> buildEmoteMessageItem(messageContent,
informationData,
event.annotations?.editSummary,
highlight,
callback)
is MessageTextContent -> buildTextMessageItem(event.root.sendState,
messageContent,
informationData,
event.annotations?.editSummary,
highlight,
callback
)
@ -298,7 +295,6 @@ class MessageItemFactory @Inject constructor(
private fun buildTextMessageItem(sendState: SendState,
messageContent: MessageTextContent,
informationData: MessageInformationData,
editSummary: EditAggregatedSummary?,
highlight: Boolean,
callback: TimelineEventController.Callback?): MessageTextItem? {

@ -311,7 +307,7 @@ class MessageItemFactory @Inject constructor(
return MessageTextItem_()
.apply {
if (informationData.hasBeenEdited) {
val spannable = annotateWithEdited(linkifiedBody, callback, informationData, editSummary)
val spannable = annotateWithEdited(linkifiedBody, callback, informationData)
message(spannable)
} else {
message(linkifiedBody)
@ -338,8 +334,7 @@ class MessageItemFactory @Inject constructor(

private fun annotateWithEdited(linkifiedBody: CharSequence,
callback: TimelineEventController.Callback?,
informationData: MessageInformationData,
editSummary: EditAggregatedSummary?): SpannableStringBuilder {
informationData: MessageInformationData): SpannableStringBuilder {
val spannable = SpannableStringBuilder()
spannable.append(linkifiedBody)
val editedSuffix = stringProvider.getString(R.string.edited_suffix)
@ -356,7 +351,7 @@ class MessageItemFactory @Inject constructor(
spannable.setSpan(RelativeSizeSpan(.9f), editStart, editEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE)
spannable.setSpan(object : ClickableSpan() {
override fun onClick(widget: View?) {
callback?.onEditedDecorationClicked(informationData, editSummary)
callback?.onEditedDecorationClicked(informationData)
}

override fun updateDrawState(ds: TextPaint?) {
@ -408,7 +403,6 @@ class MessageItemFactory @Inject constructor(

private fun buildEmoteMessageItem(messageContent: MessageEmoteContent,
informationData: MessageInformationData,
editSummary: EditAggregatedSummary?,
highlight: Boolean,
callback: TimelineEventController.Callback?): MessageTextItem? {

@ -419,7 +413,7 @@ class MessageItemFactory @Inject constructor(
return MessageTextItem_()
.apply {
if (informationData.hasBeenEdited) {
val spannable = annotateWithEdited(message, callback, informationData, editSummary)
val spannable = annotateWithEdited(message, callback, informationData)
message(spannable)
} else {
message(message)

View File

@ -18,6 +18,7 @@ package im.vector.riotx.features.home.room.detail.timeline.util

import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.api.session.room.timeline.hasBeenEdited
import im.vector.riotx.core.extensions.localDateTime
import im.vector.riotx.core.resources.ColorProvider
import im.vector.riotx.core.utils.isSingleEmoji
@ -59,8 +60,6 @@ class MessageInformationDataFactory @Inject constructor(private val timelineDate
?: ""))
}

val hasBeenEdited = event.annotations?.editSummary != null

return MessageInformationData(
eventId = eventId,
senderId = event.root.senderId ?: "",
@ -74,7 +73,7 @@ class MessageInformationDataFactory @Inject constructor(private val timelineDate
?.map {
ReactionInfoData(it.key, it.count, it.addedByMe, it.localEchoEvents.isEmpty())
},
hasBeenEdited = hasBeenEdited,
hasBeenEdited = event.hasBeenEdited(),
hasPendingEdits = event.annotations?.editSummary?.localEchos?.any() ?: false
)
}

View File

@ -0,0 +1,46 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="22dp"
android:height="16dp"
android:viewportWidth="22"
android:viewportHeight="16">
<path
android:pathData="M8,5.5L1,5.5"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#9E9E9E"
android:fillType="evenOdd"
android:strokeLineCap="round"/>
<path
android:pathData="M12,1.5L1,1.5"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#9E9E9E"
android:fillType="evenOdd"
android:strokeLineCap="round"/>
<path
android:pathData="M5,9.5L1,9.5"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#9E9E9E"
android:fillType="evenOdd"
android:strokeLineCap="round"/>
<path
android:pathData="M3,13.5L1,13.5"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#9E9E9E"
android:fillType="evenOdd"
android:strokeLineCap="round"/>
<path
android:pathData="M17.5776,1.6025C18.3598,0.7992 19.6278,0.7992 20.4099,1.6025C21.1921,2.4058 21.1921,3.7083 20.4099,4.5116L11.441,13.7237L7.6646,14.6934L8.6087,10.8146L17.5776,1.6025Z"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#9E9E9E"
android:fillType="evenOdd"
android:strokeLineCap="round"/>
</vector>

View File

@ -9,4 +9,7 @@
<string name="direct_room_filter_hint">"Filter by username or ID…"</string>

<string name="joining_room">"Joining room…"</string>

<string name="message_view_edit_history">View Edit History</string>

</resources>