diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/utils/messagesummary/DefaultMessageSummaryFormatter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/utils/messagesummary/DefaultMessageSummaryFormatter.kt index 1f44d3dd1f..8a05770942 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/utils/messagesummary/DefaultMessageSummaryFormatter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/utils/messagesummary/DefaultMessageSummaryFormatter.kt @@ -26,6 +26,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent 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.libraries.core.extensions.toSafeLength import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.di.RoomScope import io.element.android.libraries.ui.strings.CommonStrings @@ -35,11 +36,6 @@ import javax.inject.Inject class DefaultMessageSummaryFormatter @Inject constructor( @ApplicationContext private val context: Context, ) : MessageSummaryFormatter { - companion object { - // Max characters to display in the summary message. This works around https://github.com/element-hq/element-x-android/issues/2105 - private const val MAX_SAFE_LENGTH = 500 - } - override fun format(event: TimelineItem.Event): String { return when (event.content) { is TimelineItemTextBasedContent -> event.content.plainText @@ -58,6 +54,8 @@ class DefaultMessageSummaryFormatter @Inject constructor( is TimelineItemAudioContent -> context.getString(CommonStrings.common_audio) is TimelineItemLegacyCallInviteContent -> context.getString(CommonStrings.common_unsupported_call) is TimelineItemCallNotifyContent -> context.getString(CommonStrings.common_call_started) - }.take(MAX_SAFE_LENGTH) + } + // Truncate the message to a safe length to avoid crashes in Compose + .toSafeLength() } } diff --git a/libraries/core/src/main/kotlin/io/element/android/libraries/core/extensions/BasicExtensions.kt b/libraries/core/src/main/kotlin/io/element/android/libraries/core/extensions/BasicExtensions.kt index 48dbcade47..abd6e10d1f 100644 --- a/libraries/core/src/main/kotlin/io/element/android/libraries/core/extensions/BasicExtensions.kt +++ b/libraries/core/src/main/kotlin/io/element/android/libraries/core/extensions/BasicExtensions.kt @@ -98,3 +98,22 @@ fun String.ensureEndsLeftToRight() = if (containsRtLOverride()) "$this$LTR_OVERR fun String.containsRtLOverride() = contains(RTL_OVERRIDE_CHAR) fun String.filterDirectionOverrides() = filterNot { it == RTL_OVERRIDE_CHAR || it == LTR_OVERRIDE_CHAR } + +/** + * This works around https://github.com/element-hq/element-x-android/issues/2105. + * @param maxLength Max characters to retrieve. Defaults to `500`. + * @param ellipsize Whether to add an ellipsis (`…`) char at the end or not. Defaults to `false`. + * @return The string truncated to [maxLength] characters, with an optional ellipsis if larger. + */ +fun String.toSafeLength( + maxLength: Int = 500, + ellipsize: Boolean = false, +): String { + return if (ellipsize) { + ellipsize(maxLength) + } else if (length > maxLength) { + take(maxLength) + } else { + this + } +} diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToView.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToView.kt index 3fb4ddd74a..d3135d9fd8 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToView.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToView.kt @@ -31,6 +31,7 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme +import io.element.android.libraries.core.extensions.toSafeLength import io.element.android.libraries.designsystem.atomic.atoms.PlaceholderAtom import io.element.android.libraries.designsystem.icons.CompoundDrawables import io.element.android.libraries.designsystem.preview.ElementPreview @@ -152,8 +153,10 @@ private fun ReplyToContentText(metadata: InReplyToMetadata?) { val text = when (metadata) { InReplyToMetadata.Redacted -> stringResource(id = CommonStrings.common_message_removed) InReplyToMetadata.UnableToDecrypt -> stringResource(id = CommonStrings.common_waiting_for_decryption_key) - is InReplyToMetadata.Text -> metadata.text - is InReplyToMetadata.Thumbnail -> metadata.text + // Add a limit to the text length to avoid a crash in Compose + is InReplyToMetadata.Text -> metadata.text.toSafeLength() + // Add a limit to the text length to avoid a crash in Compose + is InReplyToMetadata.Thumbnail -> metadata.text.toSafeLength() null -> "" } val iconResourceId = when (metadata) {