Fix pillification not working for non formatted message bodies (#3201)
* Fix pillification not working for non formatted message bodies Pure Markdown bodies weren't being 'pillified' so their mentions were turned into UI elements in the timeline. A new `pillifiedBody` property was added to `TimelineItemTextBasedContent` to fix this. * Use shorter version of `textWithMentions` computation
This commit is contained in:
parent
27226e382a
commit
a6b6d2ab7a
20 changed files with 90 additions and 37 deletions
|
|
@ -571,7 +571,7 @@ internal fun TimelineItemEventRowPreview() = ElementPreview {
|
||||||
event = aTimelineItemEvent(
|
event = aTimelineItemEvent(
|
||||||
senderDisplayName = "Sender with a super long name that should ellipsize",
|
senderDisplayName = "Sender with a super long name that should ellipsize",
|
||||||
isMine = isMine,
|
isMine = isMine,
|
||||||
content = aTimelineItemTextContent().copy(
|
content = aTimelineItemTextContent(
|
||||||
body = "A long text which will be displayed on several lines and" +
|
body = "A long text which will be displayed on several lines and" +
|
||||||
" hopefully can be manually adjusted to test different behaviors."
|
" hopefully can be manually adjusted to test different behaviors."
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ internal fun TimelineItemEventRowForDirectRoomPreview() = ElementPreview {
|
||||||
ATimelineItemEventRow(
|
ATimelineItemEventRow(
|
||||||
event = aTimelineItemEvent(
|
event = aTimelineItemEvent(
|
||||||
isMine = it,
|
isMine = it,
|
||||||
content = aTimelineItemTextContent().copy(
|
content = aTimelineItemTextContent(
|
||||||
body = "A long text which will be displayed on several lines and" +
|
body = "A long text which will be displayed on several lines and" +
|
||||||
" hopefully can be manually adjusted to test different behaviors."
|
" hopefully can be manually adjusted to test different behaviors."
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ internal fun TimelineItemEventRowTimestampPreview(
|
||||||
event = event.copy(
|
event = event.copy(
|
||||||
content = oldContent.copy(
|
content = oldContent.copy(
|
||||||
body = str,
|
body = str,
|
||||||
|
pillifiedBody = str,
|
||||||
),
|
),
|
||||||
reactionsState = aTimelineItemReactions(count = 0),
|
reactionsState = aTimelineItemReactions(count = 0),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ internal fun TimelineItemEventRowWithManyReactionsPreview() = ElementPreview {
|
||||||
ATimelineItemEventRow(
|
ATimelineItemEventRow(
|
||||||
event = aTimelineItemEvent(
|
event = aTimelineItemEvent(
|
||||||
isMine = isMine,
|
isMine = isMine,
|
||||||
content = aTimelineItemTextContent().copy(
|
content = aTimelineItemTextContent(
|
||||||
body = "A couple of multi-line messages with many reactions attached." +
|
body = "A couple of multi-line messages with many reactions attached." +
|
||||||
" One sent by me and another from someone else."
|
" One sent by me and another from someone else."
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -41,9 +41,7 @@ internal fun TimelineItemEventRowWithRRPreview(
|
||||||
event = aTimelineItemEvent(
|
event = aTimelineItemEvent(
|
||||||
isMine = false,
|
isMine = false,
|
||||||
sendState = null,
|
sendState = null,
|
||||||
content = aTimelineItemTextContent().copy(
|
content = aTimelineItemTextContent(body = "A message from someone else"),
|
||||||
body = "A message from someone else"
|
|
||||||
),
|
|
||||||
timelineItemReactions = aTimelineItemReactions(count = 0),
|
timelineItemReactions = aTimelineItemReactions(count = 0),
|
||||||
readReceiptState = TimelineItemReadReceipts(state.receipts),
|
readReceiptState = TimelineItemReadReceipts(state.receipts),
|
||||||
),
|
),
|
||||||
|
|
@ -55,9 +53,7 @@ internal fun TimelineItemEventRowWithRRPreview(
|
||||||
event = aTimelineItemEvent(
|
event = aTimelineItemEvent(
|
||||||
isMine = true,
|
isMine = true,
|
||||||
sendState = state.sendState,
|
sendState = state.sendState,
|
||||||
content = aTimelineItemTextContent().copy(
|
content = aTimelineItemTextContent(body = "A message from me"),
|
||||||
body = "A message from me"
|
|
||||||
),
|
|
||||||
timelineItemReactions = aTimelineItemReactions(count = 0),
|
timelineItemReactions = aTimelineItemReactions(count = 0),
|
||||||
readReceiptState = TimelineItemReadReceipts(state.receipts),
|
readReceiptState = TimelineItemReadReceipts(state.receipts),
|
||||||
),
|
),
|
||||||
|
|
@ -69,9 +65,7 @@ internal fun TimelineItemEventRowWithRRPreview(
|
||||||
event = aTimelineItemEvent(
|
event = aTimelineItemEvent(
|
||||||
isMine = true,
|
isMine = true,
|
||||||
sendState = state.sendState,
|
sendState = state.sendState,
|
||||||
content = aTimelineItemTextContent().copy(
|
content = aTimelineItemTextContent(body = "A last message from me"),
|
||||||
body = "A last message from me"
|
|
||||||
),
|
|
||||||
timelineItemReactions = aTimelineItemReactions(count = 0),
|
timelineItemReactions = aTimelineItemReactions(count = 0),
|
||||||
readReceiptState = TimelineItemReadReceipts(state.receipts),
|
readReceiptState = TimelineItemReadReceipts(state.receipts),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -48,9 +48,7 @@ internal fun TimelineItemEventRowWithReplyContentToPreview(
|
||||||
event = aTimelineItemEvent(
|
event = aTimelineItemEvent(
|
||||||
isMine = it,
|
isMine = it,
|
||||||
timelineItemReactions = aTimelineItemReactions(count = 0),
|
timelineItemReactions = aTimelineItemReactions(count = 0),
|
||||||
content = aTimelineItemTextContent().copy(
|
content = aTimelineItemTextContent(body = "A reply."),
|
||||||
body = "A reply."
|
|
||||||
),
|
|
||||||
inReplyTo = inReplyToDetails,
|
inReplyTo = inReplyToDetails,
|
||||||
displayNameAmbiguous = displayNameAmbiguous,
|
displayNameAmbiguous = displayNameAmbiguous,
|
||||||
groupPosition = TimelineItemGroupPosition.First,
|
groupPosition = TimelineItemGroupPosition.First,
|
||||||
|
|
|
||||||
|
|
@ -77,14 +77,13 @@ internal fun getTextWithResolvedMentions(content: TimelineItemTextBasedContent):
|
||||||
val userProfileCache = LocalRoomMemberProfilesCache.current
|
val userProfileCache = LocalRoomMemberProfilesCache.current
|
||||||
val lastCacheUpdate by userProfileCache.lastCacheUpdate.collectAsState()
|
val lastCacheUpdate by userProfileCache.lastCacheUpdate.collectAsState()
|
||||||
val mentionSpanTheme = LocalMentionSpanTheme.current
|
val mentionSpanTheme = LocalMentionSpanTheme.current
|
||||||
val formattedBody = remember(content.formattedBody, mentionSpanTheme, lastCacheUpdate) {
|
val formattedBody = content.formattedBody ?: content.pillifiedBody
|
||||||
content.formattedBody?.let { formattedBody ->
|
val textWithMentions = remember(formattedBody, mentionSpanTheme, lastCacheUpdate) {
|
||||||
updateMentionSpans(formattedBody, userProfileCache)
|
updateMentionSpans(formattedBody, userProfileCache)
|
||||||
mentionSpanTheme.updateMentionStyles(formattedBody)
|
mentionSpanTheme.updateMentionStyles(formattedBody)
|
||||||
formattedBody
|
formattedBody
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return SpannableString(formattedBody ?: content.body)
|
return SpannableString(textWithMentions)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateMentionSpans(text: CharSequence, cache: RoomMemberProfilesCache): Boolean {
|
private fun updateMentionSpans(text: CharSequence, cache: RoomMemberProfilesCache): Boolean {
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent
|
||||||
|
import io.element.android.features.messages.impl.utils.TextPillificationHelper
|
||||||
import io.element.android.libraries.androidutils.filesize.FileSizeFormatter
|
import io.element.android.libraries.androidutils.filesize.FileSizeFormatter
|
||||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||||
|
|
@ -69,6 +70,7 @@ class TimelineItemContentMessageFactory @Inject constructor(
|
||||||
private val featureFlagService: FeatureFlagService,
|
private val featureFlagService: FeatureFlagService,
|
||||||
private val htmlConverterProvider: HtmlConverterProvider,
|
private val htmlConverterProvider: HtmlConverterProvider,
|
||||||
private val permalinkParser: PermalinkParser,
|
private val permalinkParser: PermalinkParser,
|
||||||
|
private val textPillificationHelper: TextPillificationHelper,
|
||||||
) {
|
) {
|
||||||
suspend fun create(
|
suspend fun create(
|
||||||
content: MessageContent,
|
content: MessageContent,
|
||||||
|
|
@ -126,6 +128,7 @@ class TimelineItemContentMessageFactory @Inject constructor(
|
||||||
val body = messageType.body.trimEnd()
|
val body = messageType.body.trimEnd()
|
||||||
TimelineItemTextContent(
|
TimelineItemTextContent(
|
||||||
body = body,
|
body = body,
|
||||||
|
pillifiedBody = textPillificationHelper.pillify(body),
|
||||||
htmlDocument = null,
|
htmlDocument = null,
|
||||||
plainText = body,
|
plainText = body,
|
||||||
formattedBody = null,
|
formattedBody = null,
|
||||||
|
|
@ -215,6 +218,7 @@ class TimelineItemContentMessageFactory @Inject constructor(
|
||||||
val body = messageType.body.trimEnd()
|
val body = messageType.body.trimEnd()
|
||||||
TimelineItemTextContent(
|
TimelineItemTextContent(
|
||||||
body = body,
|
body = body,
|
||||||
|
pillifiedBody = textPillificationHelper.pillify(body),
|
||||||
htmlDocument = messageType.formatted?.toHtmlDocument(permalinkParser = permalinkParser),
|
htmlDocument = messageType.formatted?.toHtmlDocument(permalinkParser = permalinkParser),
|
||||||
formattedBody = parseHtml(messageType.formatted) ?: body.withLinks(),
|
formattedBody = parseHtml(messageType.formatted) ?: body.withLinks(),
|
||||||
isEdited = content.isEdited,
|
isEdited = content.isEdited,
|
||||||
|
|
@ -224,6 +228,7 @@ class TimelineItemContentMessageFactory @Inject constructor(
|
||||||
val body = messageType.body.trimEnd()
|
val body = messageType.body.trimEnd()
|
||||||
TimelineItemTextContent(
|
TimelineItemTextContent(
|
||||||
body = body,
|
body = body,
|
||||||
|
pillifiedBody = textPillificationHelper.pillify(body),
|
||||||
htmlDocument = null,
|
htmlDocument = null,
|
||||||
formattedBody = body.withLinks(),
|
formattedBody = body.withLinks(),
|
||||||
isEdited = content.isEdited,
|
isEdited = content.isEdited,
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import org.jsoup.nodes.Document
|
||||||
|
|
||||||
data class TimelineItemEmoteContent(
|
data class TimelineItemEmoteContent(
|
||||||
override val body: String,
|
override val body: String,
|
||||||
|
override val pillifiedBody: CharSequence = body,
|
||||||
override val htmlDocument: Document?,
|
override val htmlDocument: Document?,
|
||||||
override val plainText: String = htmlDocument?.toPlainText() ?: body,
|
override val plainText: String = htmlDocument?.toPlainText() ?: body,
|
||||||
override val formattedBody: CharSequence?,
|
override val formattedBody: CharSequence?,
|
||||||
|
|
|
||||||
|
|
@ -84,8 +84,12 @@ fun aTimelineItemNoticeContent() = TimelineItemNoticeContent(
|
||||||
|
|
||||||
fun aTimelineItemRedactedContent() = TimelineItemRedactedContent
|
fun aTimelineItemRedactedContent() = TimelineItemRedactedContent
|
||||||
|
|
||||||
fun aTimelineItemTextContent() = TimelineItemTextContent(
|
fun aTimelineItemTextContent(
|
||||||
body = "Text",
|
body: String = "Text",
|
||||||
|
pillifiedBody: CharSequence = body,
|
||||||
|
) = TimelineItemTextContent(
|
||||||
|
body = body,
|
||||||
|
pillifiedBody = pillifiedBody,
|
||||||
htmlDocument = null,
|
htmlDocument = null,
|
||||||
formattedBody = null,
|
formattedBody = null,
|
||||||
isEdited = false,
|
isEdited = false,
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import org.jsoup.nodes.Document
|
||||||
|
|
||||||
data class TimelineItemNoticeContent(
|
data class TimelineItemNoticeContent(
|
||||||
override val body: String,
|
override val body: String,
|
||||||
|
override val pillifiedBody: CharSequence = body,
|
||||||
override val htmlDocument: Document?,
|
override val htmlDocument: Document?,
|
||||||
override val plainText: String = htmlDocument?.toPlainText() ?: body,
|
override val plainText: String = htmlDocument?.toPlainText() ?: body,
|
||||||
override val formattedBody: CharSequence?,
|
override val formattedBody: CharSequence?,
|
||||||
|
|
|
||||||
|
|
@ -19,13 +19,30 @@ package io.element.android.features.messages.impl.timeline.model.event
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import org.jsoup.nodes.Document
|
import org.jsoup.nodes.Document
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 {
|
||||||
|
/** The raw body of the event, in Markdown format. */
|
||||||
val body: String
|
val body: String
|
||||||
|
|
||||||
|
/** The body of the event, with mentions replaced by their pillified version. */
|
||||||
|
val pillifiedBody: CharSequence
|
||||||
|
|
||||||
|
/** The parsed HTML DOM of the formatted event body. */
|
||||||
val htmlDocument: Document?
|
val htmlDocument: Document?
|
||||||
|
|
||||||
|
/** The formatted body of the event, already parsed and with the DOM translated to Android spans. */
|
||||||
val formattedBody: CharSequence?
|
val formattedBody: CharSequence?
|
||||||
|
|
||||||
|
/** 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
|
val isEdited: Boolean
|
||||||
|
|
||||||
|
/** The raw HTML body of the event. */
|
||||||
val htmlBody: String?
|
val htmlBody: String?
|
||||||
get() = htmlDocument?.body()?.html()
|
get() = htmlDocument?.body()?.html()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import org.jsoup.nodes.Document
|
||||||
|
|
||||||
data class TimelineItemTextContent(
|
data class TimelineItemTextContent(
|
||||||
override val body: String,
|
override val body: String,
|
||||||
|
override val pillifiedBody: CharSequence = body,
|
||||||
override val htmlDocument: Document?,
|
override val htmlDocument: Document?,
|
||||||
override val plainText: String = htmlDocument?.toPlainText() ?: body,
|
override val plainText: String = htmlDocument?.toPlainText() ?: body,
|
||||||
override val formattedBody: CharSequence?,
|
override val formattedBody: CharSequence?,
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ package io.element.android.features.messages.impl.utils
|
||||||
import android.text.Spannable
|
import android.text.Spannable
|
||||||
import android.text.SpannableStringBuilder
|
import android.text.SpannableStringBuilder
|
||||||
import androidx.core.text.getSpans
|
import androidx.core.text.getSpans
|
||||||
|
import com.squareup.anvil.annotations.ContributesBinding
|
||||||
|
import io.element.android.libraries.di.RoomScope
|
||||||
import io.element.android.libraries.matrix.api.core.MatrixPatternType
|
import io.element.android.libraries.matrix.api.core.MatrixPatternType
|
||||||
import io.element.android.libraries.matrix.api.core.MatrixPatterns
|
import io.element.android.libraries.matrix.api.core.MatrixPatterns
|
||||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||||
|
|
@ -30,14 +32,19 @@ import io.element.android.libraries.textcomposer.mentions.MentionSpan
|
||||||
import io.element.android.libraries.textcomposer.mentions.MentionSpanProvider
|
import io.element.android.libraries.textcomposer.mentions.MentionSpanProvider
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class TextPillificationHelper @Inject constructor(
|
interface TextPillificationHelper {
|
||||||
|
fun pillify(text: CharSequence): CharSequence
|
||||||
|
}
|
||||||
|
|
||||||
|
@ContributesBinding(RoomScope::class)
|
||||||
|
class DefaultTextPillificationHelper @Inject constructor(
|
||||||
private val mentionSpanProvider: MentionSpanProvider,
|
private val mentionSpanProvider: MentionSpanProvider,
|
||||||
private val permalinkBuilder: PermalinkBuilder,
|
private val permalinkBuilder: PermalinkBuilder,
|
||||||
private val permalinkParser: PermalinkParser,
|
private val permalinkParser: PermalinkParser,
|
||||||
private val roomMemberProfilesCache: RoomMemberProfilesCache,
|
private val roomMemberProfilesCache: RoomMemberProfilesCache,
|
||||||
) {
|
) : TextPillificationHelper {
|
||||||
@Suppress("LoopWithTooManyJumpStatements")
|
@Suppress("LoopWithTooManyJumpStatements")
|
||||||
fun pillify(text: CharSequence): CharSequence {
|
override fun pillify(text: CharSequence): CharSequence {
|
||||||
val matches = MatrixPatterns.findPatterns(text, permalinkParser).sortedByDescending { it.end }
|
val matches = MatrixPatterns.findPatterns(text, permalinkParser).sortedByDescending { it.end }
|
||||||
if (matches.isEmpty()) return text
|
if (matches.isEmpty()) return text
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemPollContent
|
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemPollContent
|
||||||
import io.element.android.features.messages.impl.typing.TypingNotificationPresenter
|
import io.element.android.features.messages.impl.typing.TypingNotificationPresenter
|
||||||
import io.element.android.features.messages.impl.utils.TextPillificationHelper
|
import io.element.android.features.messages.impl.utils.FakeTextPillificationHelper
|
||||||
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerPlayer
|
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerPlayer
|
||||||
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerPresenter
|
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerPresenter
|
||||||
import io.element.android.features.messages.impl.voicemessages.timeline.FakeRedactedVoiceMessageManager
|
import io.element.android.features.messages.impl.voicemessages.timeline.FakeRedactedVoiceMessageManager
|
||||||
|
|
@ -787,7 +787,7 @@ class MessagesPresenterTest {
|
||||||
timelineController = TimelineController(matrixRoom),
|
timelineController = TimelineController(matrixRoom),
|
||||||
draftService = FakeComposerDraftService(),
|
draftService = FakeComposerDraftService(),
|
||||||
mentionSpanProvider = mentionSpanProvider,
|
mentionSpanProvider = mentionSpanProvider,
|
||||||
pillificationHelper = TextPillificationHelper(mentionSpanProvider, FakePermalinkBuilder(), FakePermalinkParser(), RoomMemberProfilesCache()),
|
pillificationHelper = FakeTextPillificationHelper(),
|
||||||
roomMemberProfilesCache = RoomMemberProfilesCache(),
|
roomMemberProfilesCache = RoomMemberProfilesCache(),
|
||||||
).apply {
|
).apply {
|
||||||
showTextFormatting = true
|
showTextFormatting = true
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ import io.element.android.features.messages.impl.timeline.factories.event.Timeli
|
||||||
import io.element.android.features.messages.impl.timeline.factories.virtual.TimelineItemDaySeparatorFactory
|
import io.element.android.features.messages.impl.timeline.factories.virtual.TimelineItemDaySeparatorFactory
|
||||||
import io.element.android.features.messages.impl.timeline.factories.virtual.TimelineItemVirtualFactory
|
import io.element.android.features.messages.impl.timeline.factories.virtual.TimelineItemVirtualFactory
|
||||||
import io.element.android.features.messages.impl.timeline.groups.TimelineItemGrouper
|
import io.element.android.features.messages.impl.timeline.groups.TimelineItemGrouper
|
||||||
|
import io.element.android.features.messages.impl.utils.FakeTextPillificationHelper
|
||||||
import io.element.android.features.messages.test.timeline.FakeHtmlConverterProvider
|
import io.element.android.features.messages.test.timeline.FakeHtmlConverterProvider
|
||||||
import io.element.android.features.poll.test.pollcontent.FakePollContentStateFactory
|
import io.element.android.features.poll.test.pollcontent.FakePollContentStateFactory
|
||||||
import io.element.android.libraries.androidutils.filesize.FakeFileSizeFormatter
|
import io.element.android.libraries.androidutils.filesize.FakeFileSizeFormatter
|
||||||
|
|
@ -62,6 +63,7 @@ internal fun TestScope.aTimelineItemsFactory(
|
||||||
featureFlagService = FakeFeatureFlagService(),
|
featureFlagService = FakeFeatureFlagService(),
|
||||||
htmlConverterProvider = FakeHtmlConverterProvider(),
|
htmlConverterProvider = FakeHtmlConverterProvider(),
|
||||||
permalinkParser = FakePermalinkParser(),
|
permalinkParser = FakePermalinkParser(),
|
||||||
|
textPillificationHelper = FakeTextPillificationHelper(),
|
||||||
),
|
),
|
||||||
redactedMessageFactory = TimelineItemContentRedactedFactory(),
|
redactedMessageFactory = TimelineItemContentRedactedFactory(),
|
||||||
stickerFactory = TimelineItemContentStickerFactory(
|
stickerFactory = TimelineItemContentStickerFactory(
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ import io.element.android.features.messages.impl.messagecomposer.MessageComposer
|
||||||
import io.element.android.features.messages.impl.messagecomposer.MessageComposerPresenter
|
import io.element.android.features.messages.impl.messagecomposer.MessageComposerPresenter
|
||||||
import io.element.android.features.messages.impl.messagecomposer.MessageComposerState
|
import io.element.android.features.messages.impl.messagecomposer.MessageComposerState
|
||||||
import io.element.android.features.messages.impl.timeline.TimelineController
|
import io.element.android.features.messages.impl.timeline.TimelineController
|
||||||
|
import io.element.android.features.messages.impl.utils.FakeTextPillificationHelper
|
||||||
import io.element.android.features.messages.impl.utils.TextPillificationHelper
|
import io.element.android.features.messages.impl.utils.TextPillificationHelper
|
||||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||||
|
|
@ -1362,12 +1363,7 @@ class MessageComposerPresenterTest {
|
||||||
permalinkParser: PermalinkParser = FakePermalinkParser(),
|
permalinkParser: PermalinkParser = FakePermalinkParser(),
|
||||||
mentionSpanProvider: MentionSpanProvider = MentionSpanProvider(permalinkParser),
|
mentionSpanProvider: MentionSpanProvider = MentionSpanProvider(permalinkParser),
|
||||||
roomMemberProfilesCache: RoomMemberProfilesCache = RoomMemberProfilesCache(),
|
roomMemberProfilesCache: RoomMemberProfilesCache = RoomMemberProfilesCache(),
|
||||||
textPillificationHelper: TextPillificationHelper = TextPillificationHelper(
|
textPillificationHelper: TextPillificationHelper = FakeTextPillificationHelper(),
|
||||||
mentionSpanProvider = mentionSpanProvider,
|
|
||||||
permalinkBuilder = permalinkBuilder,
|
|
||||||
permalinkParser = permalinkParser,
|
|
||||||
roomMemberProfilesCache = roomMemberProfilesCache,
|
|
||||||
),
|
|
||||||
isRichTextEditorEnabled: Boolean = true,
|
isRichTextEditorEnabled: Boolean = true,
|
||||||
draftService: ComposerDraftService = FakeComposerDraftService(),
|
draftService: ComposerDraftService = FakeComposerDraftService(),
|
||||||
) = MessageComposerPresenter(
|
) = MessageComposerPresenter(
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent
|
||||||
|
import io.element.android.features.messages.impl.utils.FakeTextPillificationHelper
|
||||||
import io.element.android.features.messages.test.timeline.FakeHtmlConverterProvider
|
import io.element.android.features.messages.test.timeline.FakeHtmlConverterProvider
|
||||||
import io.element.android.libraries.androidutils.filesize.FakeFileSizeFormatter
|
import io.element.android.libraries.androidutils.filesize.FakeFileSizeFormatter
|
||||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||||
|
|
@ -697,6 +698,7 @@ class TimelineItemContentMessageFactoryTest {
|
||||||
featureFlagService = featureFlagService,
|
featureFlagService = featureFlagService,
|
||||||
htmlConverterProvider = FakeHtmlConverterProvider(htmlConverterTransform),
|
htmlConverterProvider = FakeHtmlConverterProvider(htmlConverterTransform),
|
||||||
permalinkParser = permalinkParser,
|
permalinkParser = permalinkParser,
|
||||||
|
textPillificationHelper = FakeTextPillificationHelper(),
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun createStickerContent(
|
private fun createStickerContent(
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
class TextPillificationHelperTest {
|
class DefaultTextPillificationHelperTest {
|
||||||
@Test
|
@Test
|
||||||
fun `pillify - adds pills for user ids`() {
|
fun `pillify - adds pills for user ids`() {
|
||||||
val text = "A @user:server.com"
|
val text = "A @user:server.com"
|
||||||
|
|
@ -119,7 +119,7 @@ class TextPillificationHelperTest {
|
||||||
permalinkBuilder: FakePermalinkBuilder = FakePermalinkBuilder(),
|
permalinkBuilder: FakePermalinkBuilder = FakePermalinkBuilder(),
|
||||||
mentionSpanProvider: MentionSpanProvider = MentionSpanProvider(permalinkparser),
|
mentionSpanProvider: MentionSpanProvider = MentionSpanProvider(permalinkparser),
|
||||||
roomMemberProfilesCache: RoomMemberProfilesCache = RoomMemberProfilesCache(),
|
roomMemberProfilesCache: RoomMemberProfilesCache = RoomMemberProfilesCache(),
|
||||||
) = TextPillificationHelper(
|
) = DefaultTextPillificationHelper(
|
||||||
mentionSpanProvider = mentionSpanProvider,
|
mentionSpanProvider = mentionSpanProvider,
|
||||||
permalinkBuilder = permalinkBuilder,
|
permalinkBuilder = permalinkBuilder,
|
||||||
permalinkParser = permalinkparser,
|
permalinkParser = permalinkparser,
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.features.messages.impl.utils
|
||||||
|
|
||||||
|
class FakeTextPillificationHelper(
|
||||||
|
private val pillifyLambda: (CharSequence) -> CharSequence = { it }
|
||||||
|
) : TextPillificationHelper {
|
||||||
|
override fun pillify(text: CharSequence): CharSequence {
|
||||||
|
return pillifyLambda(text)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue