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:
commit
80e2fc0ca3
|
@ -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:
|
||||
-
|
||||
|
|
|
@ -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 ?: ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
Loading…
Reference in New Issue