Merge pull request #3904 from element-hq/feature/bma/editedCaption

Render edited caption.
This commit is contained in:
Benoit Marty 2024-11-20 21:30:32 +01:00 committed by GitHub
commit 51714108a3
19 changed files with 64 additions and 19 deletions

View file

@ -87,6 +87,7 @@ class TimelineItemContentMessageFactory @Inject constructor(
filename = messageType.filename, filename = messageType.filename,
caption = messageType.caption?.trimEnd(), caption = messageType.caption?.trimEnd(),
formattedCaption = parseHtml(messageType.formattedCaption) ?: messageType.caption?.withLinks(), formattedCaption = parseHtml(messageType.formattedCaption) ?: messageType.caption?.withLinks(),
isEdited = content.isEdited,
mediaSource = messageType.source, mediaSource = messageType.source,
thumbnailSource = messageType.info?.thumbnailSource, thumbnailSource = messageType.info?.thumbnailSource,
mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream, mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream,
@ -106,6 +107,7 @@ class TimelineItemContentMessageFactory @Inject constructor(
filename = messageType.filename, filename = messageType.filename,
caption = messageType.caption?.trimEnd(), caption = messageType.caption?.trimEnd(),
formattedCaption = parseHtml(messageType.formattedCaption) ?: messageType.caption?.withLinks(), formattedCaption = parseHtml(messageType.formattedCaption) ?: messageType.caption?.withLinks(),
isEdited = content.isEdited,
mediaSource = messageType.source, mediaSource = messageType.source,
thumbnailSource = messageType.info?.thumbnailSource, thumbnailSource = messageType.info?.thumbnailSource,
mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream, mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream,
@ -143,6 +145,7 @@ class TimelineItemContentMessageFactory @Inject constructor(
filename = messageType.filename, filename = messageType.filename,
caption = messageType.caption?.trimEnd(), caption = messageType.caption?.trimEnd(),
formattedCaption = parseHtml(messageType.formattedCaption) ?: messageType.caption?.withLinks(), formattedCaption = parseHtml(messageType.formattedCaption) ?: messageType.caption?.withLinks(),
isEdited = content.isEdited,
thumbnailSource = messageType.info?.thumbnailSource, thumbnailSource = messageType.info?.thumbnailSource,
videoSource = messageType.source, videoSource = messageType.source,
mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream, mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream,
@ -162,6 +165,7 @@ class TimelineItemContentMessageFactory @Inject constructor(
filename = messageType.filename, filename = messageType.filename,
caption = messageType.caption?.trimEnd(), caption = messageType.caption?.trimEnd(),
formattedCaption = parseHtml(messageType.formattedCaption) ?: messageType.caption?.withLinks(), formattedCaption = parseHtml(messageType.formattedCaption) ?: messageType.caption?.withLinks(),
isEdited = content.isEdited,
mediaSource = messageType.source, mediaSource = messageType.source,
duration = messageType.info?.duration ?: Duration.ZERO, duration = messageType.info?.duration ?: Duration.ZERO,
mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream, mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream,
@ -177,6 +181,7 @@ class TimelineItemContentMessageFactory @Inject constructor(
filename = messageType.filename, filename = messageType.filename,
caption = messageType.caption?.trimEnd(), caption = messageType.caption?.trimEnd(),
formattedCaption = parseHtml(messageType.formattedCaption) ?: messageType.caption?.withLinks(), formattedCaption = parseHtml(messageType.formattedCaption) ?: messageType.caption?.withLinks(),
isEdited = content.isEdited,
mediaSource = messageType.source, mediaSource = messageType.source,
duration = messageType.info?.duration ?: Duration.ZERO, duration = messageType.info?.duration ?: Duration.ZERO,
mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream, mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream,
@ -188,6 +193,7 @@ class TimelineItemContentMessageFactory @Inject constructor(
filename = messageType.filename, filename = messageType.filename,
caption = messageType.caption?.trimEnd(), caption = messageType.caption?.trimEnd(),
formattedCaption = parseHtml(messageType.formattedCaption) ?: messageType.caption?.withLinks(), formattedCaption = parseHtml(messageType.formattedCaption) ?: messageType.caption?.withLinks(),
isEdited = content.isEdited,
mediaSource = messageType.source, mediaSource = messageType.source,
duration = messageType.info?.duration ?: Duration.ZERO, duration = messageType.info?.duration ?: Duration.ZERO,
mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream, mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream,
@ -203,6 +209,7 @@ class TimelineItemContentMessageFactory @Inject constructor(
filename = messageType.filename, filename = messageType.filename,
caption = messageType.caption?.trimEnd(), caption = messageType.caption?.trimEnd(),
formattedCaption = parseHtml(messageType.formattedCaption) ?: messageType.caption?.withLinks(), formattedCaption = parseHtml(messageType.formattedCaption) ?: messageType.caption?.withLinks(),
isEdited = content.isEdited,
thumbnailSource = messageType.info?.thumbnailSource, thumbnailSource = messageType.info?.thumbnailSource,
fileSource = messageType.source, fileSource = messageType.source,
mimeType = messageType.info?.mimetype ?: MimeTypes.fromFileExtension(fileExtension), mimeType = messageType.info?.mimetype ?: MimeTypes.fromFileExtension(fileExtension),

View file

@ -36,6 +36,7 @@ class TimelineItemContentStickerFactory @Inject constructor(
filename = content.filename, filename = content.filename,
caption = content.body, caption = content.body,
formattedCaption = null, formattedCaption = null,
isEdited = false,
mediaSource = content.source, mediaSource = content.source,
thumbnailSource = content.info.thumbnailSource, thumbnailSource = content.info.thumbnailSource,
mimeType = content.info.mimetype ?: MimeTypes.OctetStream, mimeType = content.info.mimetype ?: MimeTypes.OctetStream,

View file

@ -15,6 +15,7 @@ data class TimelineItemAudioContent(
override val filename: String, override val filename: String,
override val caption: String?, override val caption: String?,
override val formattedCaption: CharSequence?, override val formattedCaption: CharSequence?,
override val isEdited: Boolean,
val duration: Duration, val duration: Duration,
val mediaSource: MediaSource, val mediaSource: MediaSource,
val mimeType: String, val mimeType: String,

View file

@ -25,6 +25,7 @@ fun aTimelineItemAudioContent(fileName: String = "A sound.mp3") = TimelineItemAu
filename = fileName, filename = fileName,
caption = null, caption = null,
formattedCaption = null, formattedCaption = null,
isEdited = false,
mimeType = MimeTypes.Mp3, mimeType = MimeTypes.Mp3,
formattedFileSize = "100kB", formattedFileSize = "100kB",
fileExtension = "mp3", fileExtension = "mp3",

View file

@ -14,8 +14,15 @@ sealed interface TimelineItemEventContent {
val type: String val type: String
} }
interface TimelineItemEventMutableContent {
/** Whether the event has been edited. */
val isEdited: Boolean
}
@Immutable @Immutable
sealed interface TimelineItemEventContentWithAttachment : TimelineItemEventContent { sealed interface TimelineItemEventContentWithAttachment :
TimelineItemEventContent,
TimelineItemEventMutableContent {
val filename: String val filename: String
val caption: String? val caption: String?
val formattedCaption: CharSequence? val formattedCaption: CharSequence?
@ -74,9 +81,7 @@ fun TimelineItemEventContent.canReact(): Boolean =
/** /**
* Whether the event content has been edited. * Whether the event content has been edited.
*/ */
fun TimelineItemEventContent.isEdited(): Boolean = fun TimelineItemEventContent.isEdited(): Boolean = when (this) {
when (this) { is TimelineItemEventMutableContent -> isEdited
is TimelineItemTextBasedContent -> isEdited else -> false
is TimelineItemPollContent -> isEdited }
else -> false
}

View file

@ -14,6 +14,7 @@ data class TimelineItemFileContent(
override val filename: String, override val filename: String,
override val caption: String?, override val caption: String?,
override val formattedCaption: CharSequence?, override val formattedCaption: CharSequence?,
override val isEdited: Boolean,
val fileSource: MediaSource, val fileSource: MediaSource,
val thumbnailSource: MediaSource?, val thumbnailSource: MediaSource?,
val formattedFileSize: String, val formattedFileSize: String,

View file

@ -26,6 +26,7 @@ fun aTimelineItemFileContent(
filename = fileName, filename = fileName,
caption = null, caption = null,
formattedCaption = null, formattedCaption = null,
isEdited = false,
thumbnailSource = null, thumbnailSource = null,
fileSource = MediaSource(url = ""), fileSource = MediaSource(url = ""),
mimeType = MimeTypes.Pdf, mimeType = MimeTypes.Pdf,

View file

@ -17,6 +17,7 @@ data class TimelineItemImageContent(
override val filename: String, override val filename: String,
override val caption: String?, override val caption: String?,
override val formattedCaption: CharSequence?, override val formattedCaption: CharSequence?,
override val isEdited: Boolean,
val mediaSource: MediaSource, val mediaSource: MediaSource,
val thumbnailSource: MediaSource?, val thumbnailSource: MediaSource?,
val formattedFileSize: String, val formattedFileSize: String,

View file

@ -31,6 +31,7 @@ fun aTimelineItemImageContent(
filename = filename, filename = filename,
caption = caption, caption = caption,
formattedCaption = null, formattedCaption = null,
isEdited = false,
mediaSource = MediaSource(""), mediaSource = MediaSource(""),
thumbnailSource = null, thumbnailSource = null,
mimeType = MimeTypes.IMAGE_JPEG, mimeType = MimeTypes.IMAGE_JPEG,

View file

@ -19,7 +19,8 @@ data class TimelineItemPollContent(
val answerItems: List<PollAnswerItem>, val answerItems: List<PollAnswerItem>,
val pollKind: PollKind, val pollKind: PollKind,
val isEnded: Boolean, val isEnded: Boolean,
val isEdited: Boolean override val isEdited: Boolean,
) : TimelineItemEventContent { ) : TimelineItemEventContent,
TimelineItemEventMutableContent {
override val type: String = "TimelineItemPollContent" override val type: String = "TimelineItemPollContent"
} }

View file

@ -13,6 +13,7 @@ data class TimelineItemStickerContent(
override val filename: String, override val filename: String,
override val caption: String?, override val caption: String?,
override val formattedCaption: CharSequence?, override val formattedCaption: CharSequence?,
override val isEdited: Boolean,
val mediaSource: MediaSource, val mediaSource: MediaSource,
val thumbnailSource: MediaSource?, val thumbnailSource: MediaSource?,
val formattedFileSize: String, val formattedFileSize: String,

View file

@ -29,6 +29,7 @@ fun aTimelineItemStickerContent(
filename = "a sticker.gif", filename = "a sticker.gif",
caption = "a body", caption = "a body",
formattedCaption = null, formattedCaption = null,
isEdited = false,
mediaSource = MediaSource(""), mediaSource = MediaSource(""),
thumbnailSource = null, thumbnailSource = null,
mimeType = MimeTypes.IMAGE_JPEG, mimeType = MimeTypes.IMAGE_JPEG,

View file

@ -14,7 +14,9 @@ import org.jsoup.nodes.Document
* Represents a text based content of a timeline item event (a message, a notice, an emote event...). * Represents a text based content of a timeline item event (a message, a notice, an emote event...).
*/ */
@Immutable @Immutable
sealed interface TimelineItemTextBasedContent : TimelineItemEventContent { sealed interface TimelineItemTextBasedContent :
TimelineItemEventContent,
TimelineItemEventMutableContent {
/** The raw body of the event, in Markdown format. */ /** The raw body of the event, in Markdown format. */
val body: String val body: String
@ -30,9 +32,6 @@ sealed interface TimelineItemTextBasedContent : TimelineItemEventContent {
/** The plain text version of the event body. This is the Markdown version without actual Markdown formatting. */ /** The plain text version of the event body. This is the Markdown version without actual Markdown formatting. */
val plainText: String val plainText: String
/** Whether the event has been edited. */
val isEdited: Boolean
/** The raw HTML body of the event. */ /** The raw HTML body of the event. */
val htmlBody: String? val htmlBody: String?
get() = htmlDocument?.body()?.html() get() = htmlDocument?.body()?.html()

View file

@ -14,6 +14,7 @@ data class TimelineItemVideoContent(
override val filename: String, override val filename: String,
override val caption: String?, override val caption: String?,
override val formattedCaption: CharSequence?, override val formattedCaption: CharSequence?,
override val isEdited: Boolean,
val duration: Duration, val duration: Duration,
val videoSource: MediaSource, val videoSource: MediaSource,
val thumbnailSource: MediaSource?, val thumbnailSource: MediaSource?,

View file

@ -30,6 +30,7 @@ fun aTimelineItemVideoContent(
filename = "Video.mp4", filename = "Video.mp4",
caption = null, caption = null,
formattedCaption = null, formattedCaption = null,
isEdited = false,
thumbnailSource = null, thumbnailSource = null,
blurHash = blurhash, blurHash = blurhash,
aspectRatio = aspectRatio, aspectRatio = aspectRatio,

View file

@ -17,6 +17,7 @@ data class TimelineItemVoiceContent(
override val filename: String, override val filename: String,
override val caption: String?, override val caption: String?,
override val formattedCaption: CharSequence?, override val formattedCaption: CharSequence?,
override val isEdited: Boolean,
val duration: Duration, val duration: Duration,
val mediaSource: MediaSource, val mediaSource: MediaSource,
val mimeType: String, val mimeType: String,

View file

@ -48,6 +48,7 @@ fun aTimelineItemVoiceContent(
filename = filename, filename = filename,
caption = caption, caption = caption,
formattedCaption = null, formattedCaption = null,
isEdited = false,
duration = duration, duration = duration,
mediaSource = mediaSource, mediaSource = mediaSource,
mimeType = mimeType, mimeType = mimeType,

View file

@ -318,6 +318,7 @@ class MessagesPresenterTest {
filename = "image.jpg", filename = "image.jpg",
caption = null, caption = null,
formattedCaption = null, formattedCaption = null,
isEdited = false,
mediaSource = MediaSource(AN_AVATAR_URL), mediaSource = MediaSource(AN_AVATAR_URL),
thumbnailSource = null, thumbnailSource = null,
mimeType = MimeTypes.Jpeg, mimeType = MimeTypes.Jpeg,
@ -359,6 +360,7 @@ class MessagesPresenterTest {
filename = "video.mp4", filename = "video.mp4",
caption = null, caption = null,
formattedCaption = null, formattedCaption = null,
isEdited = false,
duration = 10.milliseconds, duration = 10.milliseconds,
videoSource = MediaSource(AN_AVATAR_URL), videoSource = MediaSource(AN_AVATAR_URL),
thumbnailSource = MediaSource(AN_AVATAR_URL), thumbnailSource = MediaSource(AN_AVATAR_URL),
@ -400,6 +402,7 @@ class MessagesPresenterTest {
content = TimelineItemFileContent( content = TimelineItemFileContent(
filename = "file.pdf", filename = "file.pdf",
caption = null, caption = null,
isEdited = false,
formattedCaption = null, formattedCaption = null,
fileSource = MediaSource(AN_AVATAR_URL), fileSource = MediaSource(AN_AVATAR_URL),
thumbnailSource = MediaSource(AN_AVATAR_URL), thumbnailSource = MediaSource(AN_AVATAR_URL),

View file

@ -237,6 +237,7 @@ class TimelineItemContentMessageFactoryTest {
filename = "filename", filename = "filename",
caption = null, caption = null,
formattedCaption = null, formattedCaption = null,
isEdited = false,
duration = Duration.ZERO, duration = Duration.ZERO,
videoSource = MediaSource(url = "url", json = null), videoSource = MediaSource(url = "url", json = null),
thumbnailSource = null, thumbnailSource = null,
@ -278,7 +279,8 @@ class TimelineItemContentMessageFactoryTest {
thumbnailSource = MediaSource("url_thumbnail"), thumbnailSource = MediaSource("url_thumbnail"),
blurhash = A_BLUR_HASH, blurhash = A_BLUR_HASH,
), ),
) ),
isEdited = true,
), ),
senderDisambiguatedDisplayName = "Bob", senderDisambiguatedDisplayName = "Bob",
eventId = AN_EVENT_ID, eventId = AN_EVENT_ID,
@ -287,6 +289,7 @@ class TimelineItemContentMessageFactoryTest {
filename = "body.mp4", filename = "body.mp4",
caption = "body.mp4 caption", caption = "body.mp4 caption",
formattedCaption = SpannedString("formatted"), formattedCaption = SpannedString("formatted"),
isEdited = true,
duration = 1.minutes, duration = 1.minutes,
videoSource = MediaSource(url = "url", json = null), videoSource = MediaSource(url = "url", json = null),
thumbnailSource = MediaSource("url_thumbnail"), thumbnailSource = MediaSource("url_thumbnail"),
@ -315,6 +318,7 @@ class TimelineItemContentMessageFactoryTest {
filename = "filename", filename = "filename",
caption = null, caption = null,
formattedCaption = null, formattedCaption = null,
isEdited = false,
duration = Duration.ZERO, duration = Duration.ZERO,
mediaSource = MediaSource(url = "url", json = null), mediaSource = MediaSource(url = "url", json = null),
mimeType = MimeTypes.OctetStream, mimeType = MimeTypes.OctetStream,
@ -339,7 +343,8 @@ class TimelineItemContentMessageFactoryTest {
size = 123L, size = 123L,
mimetype = MimeTypes.Mp3, mimetype = MimeTypes.Mp3,
) )
) ),
isEdited = true,
), ),
senderDisambiguatedDisplayName = "Bob", senderDisambiguatedDisplayName = "Bob",
eventId = AN_EVENT_ID, eventId = AN_EVENT_ID,
@ -348,6 +353,7 @@ class TimelineItemContentMessageFactoryTest {
filename = "body.mp3", filename = "body.mp3",
caption = null, caption = null,
formattedCaption = null, formattedCaption = null,
isEdited = true,
duration = 1.minutes, duration = 1.minutes,
mediaSource = MediaSource(url = "url", json = null), mediaSource = MediaSource(url = "url", json = null),
mimeType = MimeTypes.Mp3, mimeType = MimeTypes.Mp3,
@ -370,6 +376,7 @@ class TimelineItemContentMessageFactoryTest {
eventId = AN_EVENT_ID, eventId = AN_EVENT_ID,
caption = null, caption = null,
formattedCaption = null, formattedCaption = null,
isEdited = false,
duration = Duration.ZERO, duration = Duration.ZERO,
mediaSource = MediaSource(url = "url", json = null), mediaSource = MediaSource(url = "url", json = null),
mimeType = MimeTypes.OctetStream, mimeType = MimeTypes.OctetStream,
@ -397,7 +404,8 @@ class TimelineItemContentMessageFactoryTest {
duration = 1.minutes, duration = 1.minutes,
waveform = persistentListOf(1f, 2f), waveform = persistentListOf(1f, 2f),
), ),
) ),
isEdited = true,
), ),
senderDisambiguatedDisplayName = "Bob", senderDisambiguatedDisplayName = "Bob",
eventId = AN_EVENT_ID, eventId = AN_EVENT_ID,
@ -407,6 +415,7 @@ class TimelineItemContentMessageFactoryTest {
filename = "body.ogg", filename = "body.ogg",
caption = null, caption = null,
formattedCaption = null, formattedCaption = null,
isEdited = true,
duration = 1.minutes, duration = 1.minutes,
mediaSource = MediaSource(url = "url", json = null), mediaSource = MediaSource(url = "url", json = null),
mimeType = MimeTypes.Ogg, mimeType = MimeTypes.Ogg,
@ -433,6 +442,7 @@ class TimelineItemContentMessageFactoryTest {
filename = "filename", filename = "filename",
caption = null, caption = null,
formattedCaption = null, formattedCaption = null,
isEdited = false,
duration = Duration.ZERO, duration = Duration.ZERO,
mediaSource = MediaSource(url = "url", json = null), mediaSource = MediaSource(url = "url", json = null),
mimeType = MimeTypes.OctetStream, mimeType = MimeTypes.OctetStream,
@ -454,6 +464,7 @@ class TimelineItemContentMessageFactoryTest {
filename = "filename", filename = "filename",
caption = "body", caption = "body",
formattedCaption = null, formattedCaption = null,
isEdited = false,
mediaSource = MediaSource(url = "url", json = null), mediaSource = MediaSource(url = "url", json = null),
thumbnailSource = null, thumbnailSource = null,
formattedFileSize = "0 Bytes", formattedFileSize = "0 Bytes",
@ -483,6 +494,7 @@ class TimelineItemContentMessageFactoryTest {
filename = "filename", filename = "filename",
caption = null, caption = null,
formattedCaption = null, formattedCaption = null,
isEdited = false,
mediaSource = MediaSource(url = "url", json = null), mediaSource = MediaSource(url = "url", json = null),
thumbnailSource = MediaSource(url = "thumbnail://url", json = null), thumbnailSource = MediaSource(url = "thumbnail://url", json = null),
formattedFileSize = "8192 Bytes", formattedFileSize = "8192 Bytes",
@ -520,15 +532,17 @@ class TimelineItemContentMessageFactoryTest {
thumbnailSource = MediaSource("url_thumbnail"), thumbnailSource = MediaSource("url_thumbnail"),
blurhash = A_BLUR_HASH, blurhash = A_BLUR_HASH,
) )
) ),
isEdited = true,
), ),
senderDisambiguatedDisplayName = "Bob", senderDisambiguatedDisplayName = "Bob",
eventId = AN_EVENT_ID, eventId = AN_EVENT_ID,
) )
val expected = TimelineItemImageContent( val expected = TimelineItemImageContent(
filename = "body.jpg", filename = "body.jpg",
formattedCaption = SpannedString("formatted"),
caption = "body.jpg caption", caption = "body.jpg caption",
formattedCaption = SpannedString("formatted"),
isEdited = true,
mediaSource = MediaSource(url = "url", json = null), mediaSource = MediaSource(url = "url", json = null),
thumbnailSource = MediaSource("url_thumbnail"), thumbnailSource = MediaSource("url_thumbnail"),
formattedFileSize = "888 Bytes", formattedFileSize = "888 Bytes",
@ -556,6 +570,7 @@ class TimelineItemContentMessageFactoryTest {
filename = "filename", filename = "filename",
caption = null, caption = null,
formattedCaption = null, formattedCaption = null,
isEdited = false,
fileSource = MediaSource(url = "url", json = null), fileSource = MediaSource(url = "url", json = null),
thumbnailSource = null, thumbnailSource = null,
formattedFileSize = "0 Bytes", formattedFileSize = "0 Bytes",
@ -586,7 +601,8 @@ class TimelineItemContentMessageFactoryTest {
), ),
thumbnailSource = MediaSource("url_thumbnail"), thumbnailSource = MediaSource("url_thumbnail"),
) )
) ),
isEdited = true,
), ),
senderDisambiguatedDisplayName = "Bob", senderDisambiguatedDisplayName = "Bob",
eventId = AN_EVENT_ID, eventId = AN_EVENT_ID,
@ -595,6 +611,7 @@ class TimelineItemContentMessageFactoryTest {
filename = "body.pdf", filename = "body.pdf",
caption = null, caption = null,
formattedCaption = null, formattedCaption = null,
isEdited = true,
fileSource = MediaSource(url = "url", json = null), fileSource = MediaSource(url = "url", json = null),
thumbnailSource = MediaSource("url_thumbnail"), thumbnailSource = MediaSource("url_thumbnail"),
formattedFileSize = "123 Bytes", formattedFileSize = "123 Bytes",