Iterate on extra padding solution.

This commit is contained in:
Benoit Marty
2023-06-23 12:36:09 +02:00
committed by Benoit Marty
parent 659d719456
commit 884aea1c80
10 changed files with 148 additions and 17 deletions

View File

@@ -33,6 +33,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.EventSendStat
import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toPersistentList
import kotlin.random.Random
fun aTimelineState(timelineItems: ImmutableList<TimelineItem> = persistentListOf()) = TimelineState(
@@ -102,6 +103,7 @@ internal fun aTimelineItemEvent(
sendState: EventSendState = EventSendState.Sent(eventId),
inReplyTo: InReplyTo? = null,
debugInfo: TimelineItemDebugInfo = aTimelineItemDebugInfo(),
timelineItemReactions: TimelineItemReactions = aTimelineItemReactions(isMine = isMine),
): TimelineItem.Event {
return TimelineItem.Event(
id = eventId.value,
@@ -110,11 +112,7 @@ internal fun aTimelineItemEvent(
senderId = UserId("@senderId:domain"),
senderAvatar = AvatarData("@senderId:domain", "sender", size = AvatarSize.TimelineSender),
content = content,
reactionsState = TimelineItemReactions(
persistentListOf(
AggregatedReaction("👍", "1", isOnMyMessage = isMine)
)
),
reactionsState = timelineItemReactions,
sentTime = "12:34",
isMine = isMine,
senderDisplayName = "Sender",
@@ -125,6 +123,19 @@ internal fun aTimelineItemEvent(
)
}
fun aTimelineItemReactions(
count: Int = 1,
isMine: Boolean = true,
): TimelineItemReactions {
return TimelineItemReactions(
reactions = buildList {
repeat(count) {
add(AggregatedReaction(key = "👍", count = (it + 1).toString(), isOnMyMessage = isMine))
}
}.toPersistentList()
)
}
internal fun aTimelineItemDebugInfo(
model: String = "Rust(Model())",
originalJson: String? = null,

View File

@@ -46,13 +46,17 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
import io.element.android.features.messages.impl.timeline.aTimelineItemReactions
import io.element.android.features.messages.impl.timeline.components.event.TimelineItemEventContentView
import io.element.android.features.messages.impl.timeline.components.event.toExtraPadding
import io.element.android.features.messages.impl.timeline.model.TimelineItem
import io.element.android.features.messages.impl.timeline.model.bubble.BubbleState
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
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.aTimelineItemImageContent
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
@@ -73,6 +77,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageT
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnail
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailInfo
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailType
import org.jsoup.Jsoup
@Composable
fun TimelineItemEventRow(
@@ -228,6 +233,7 @@ private fun MessageEventBubbleContent(
interactionSource = interactionSource,
onClick = onMessageClick,
onLongClick = onMessageLongClick,
extraPadding = event.toExtraPadding(),
modifier = modifier,
)
}
@@ -437,3 +443,44 @@ private fun ContentToPreview() {
}
}
}
@Preview
@Composable
internal fun TimelineItemEventRowTimestampLightPreview(@PreviewParameter(TimelineItemEventForTimestampViewProvider::class) event: TimelineItem.Event) =
ElementPreviewLight { ContentTimestampToPreview(event) }
@Preview
@Composable
internal fun TimelineItemEventRowTimestampDarkPreview(@PreviewParameter(TimelineItemEventForTimestampViewProvider::class) event: TimelineItem.Event) =
ElementPreviewDark { ContentTimestampToPreview(event) }
@Composable
private fun ContentTimestampToPreview(event: TimelineItem.Event) {
Column {
val oldContent = event.content as TimelineItemTextContent
listOf(
"Text",
"Text longer but displayed on 1 line",
"Text which should be rendered on several lines",
).forEach { str ->
listOf(false, true).forEach { useDocument ->
TimelineItemEventRow(
event = event.copy(
content = oldContent.copy(
body = str,
htmlDocument = if (useDocument) Jsoup.parse(str) else null,
),
reactionsState = aTimelineItemReactions(count = 0),
senderDisplayName = if (useDocument) "Document case" else "Text case",
),
isHighlighted = false,
onClick = {},
onLongClick = {},
onUserDataClick = {},
inReplyToClick = {},
onTimestampClicked = {},
)
}
}
}
}

View File

@@ -30,6 +30,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
import io.element.android.features.messages.impl.timeline.components.event.TimelineItemEventContentView
import io.element.android.features.messages.impl.timeline.components.event.noExtraPadding
import io.element.android.features.messages.impl.timeline.model.TimelineItem
import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemStateEventContent
@@ -66,6 +67,7 @@ fun TimelineItemStateEventRow(
interactionSource = interactionSource,
onClick = onClick,
onLongClick = onLongClick,
extraPadding = noExtraPadding,
modifier = Modifier.defaultTimelineContentPadding()
)
}

View File

@@ -17,10 +17,8 @@
package io.element.android.features.messages.impl.timeline.components.event
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEncryptedContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent
@@ -35,6 +33,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
fun TimelineItemEventContentView(
content: TimelineItemEventContent,
interactionSource: MutableInteractionSource,
extraPadding: ExtraPadding,
onClick: () -> Unit,
onLongClick: () -> Unit,
modifier: Modifier = Modifier
@@ -42,14 +41,17 @@ fun TimelineItemEventContentView(
when (content) {
is TimelineItemEncryptedContent -> TimelineItemEncryptedView(
content = content,
extraPadding = extraPadding,
modifier = modifier
)
is TimelineItemRedactedContent -> TimelineItemRedactedView(
content = content,
extraPadding = extraPadding,
modifier = modifier
)
is TimelineItemTextBasedContent -> TimelineItemTextView(
content = content,
extraPadding = extraPadding,
interactionSource = interactionSource,
modifier = modifier,
onTextClicked = onClick,
@@ -57,6 +59,7 @@ fun TimelineItemEventContentView(
)
is TimelineItemUnknownContent -> TimelineItemUnknownView(
content = content,
extraPadding = extraPadding,
modifier = modifier
)
is TimelineItemImageContent -> TimelineItemImageView(

View File

@@ -31,12 +31,14 @@ import io.element.android.libraries.ui.strings.CommonStrings
@Composable
fun TimelineItemEncryptedView(
content: TimelineItemEncryptedContent,
extraPadding: ExtraPadding,
modifier: Modifier = Modifier
) {
TimelineItemInformativeView(
text = stringResource(id = CommonStrings.common_decryption_error),
iconDescription = stringResource(id = CommonStrings.dialog_title_warning),
icon = Icons.Default.Warning,
extraPadding = extraPadding,
modifier = modifier
)
}
@@ -56,6 +58,7 @@ private fun ContentToPreview() {
TimelineItemEncryptedView(
content = TimelineItemEncryptedContent(
data = UnableToDecryptContent.Data.Unknown
)
),
extraPadding = noExtraPadding
)
}

View File

@@ -41,6 +41,7 @@ fun TimelineItemInformativeView(
text: String,
iconDescription: String,
icon: ImageVector,
extraPadding: ExtraPadding,
modifier: Modifier = Modifier
) {
Row(
@@ -58,7 +59,7 @@ fun TimelineItemInformativeView(
fontStyle = FontStyle.Italic,
color = MaterialTheme.colorScheme.secondary,
fontSize = 14.sp,
text = text + extraPaddingTrick
text = text + extraPadding.str
)
}
}
@@ -76,6 +77,7 @@ private fun ContentToPreview() {
TimelineItemInformativeView(
text = "Info",
iconDescription = "",
icon = Icons.Default.Delete
icon = Icons.Default.Delete,
extraPadding = noExtraPadding,
)
}

View File

@@ -30,12 +30,14 @@ import io.element.android.libraries.ui.strings.CommonStrings
@Composable
fun TimelineItemRedactedView(
content: TimelineItemRedactedContent,
extraPadding: ExtraPadding,
modifier: Modifier = Modifier
) {
TimelineItemInformativeView(
text = stringResource(id = CommonStrings.common_message_removed) + extraPaddingTrick,
text = stringResource(id = CommonStrings.common_message_removed),
iconDescription = stringResource(id = CommonStrings.common_message_removed),
icon = Icons.Default.Delete,
extraPadding = extraPadding,
modifier = modifier
)
}
@@ -52,5 +54,8 @@ internal fun TimelineItemRedactedViewDarkPreview() =
@Composable
private fun ContentToPreview() {
TimelineItemRedactedView(TimelineItemRedactedContent)
TimelineItemRedactedView(
TimelineItemRedactedContent,
extraPadding = noExtraPadding
)
}

View File

@@ -22,6 +22,9 @@ import android.text.util.Linkify.PHONE_NUMBERS
import android.text.util.Linkify.WEB_URLS
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
@@ -29,6 +32,7 @@ import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import androidx.core.text.util.LinkifyCompat
import io.element.android.features.messages.impl.timeline.components.html.HtmlDocument
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent
@@ -43,12 +47,25 @@ import io.element.android.libraries.designsystem.text.toAnnotatedString
fun TimelineItemTextView(
content: TimelineItemTextBasedContent,
interactionSource: MutableInteractionSource,
extraPadding: ExtraPadding,
modifier: Modifier = Modifier,
onTextClicked: () -> Unit = {},
onTextLongClicked: () -> Unit = {},
) {
val htmlDocument = content.htmlDocument
if (htmlDocument != null) {
// For now we ignore the extra padding for html content, so add some spacing
// below the content (as previous behavior)
Column(modifier = modifier) {
HtmlDocument(
document = htmlDocument,
modifier = Modifier,
onTextClicked = onTextClicked,
onTextLongClicked = onTextLongClicked,
interactionSource = interactionSource
)
Spacer(Modifier.height(16.dp))
}
HtmlDocument(
document = htmlDocument,
modifier = modifier,
@@ -61,7 +78,7 @@ fun TimelineItemTextView(
val linkStyle = SpanStyle(
color = LinkColor,
)
val styledText = remember(content.body) { content.body.linkify(linkStyle) + extraPaddingTrick.toAnnotatedString() }
val styledText = remember(content.body) { content.body.linkify(linkStyle) + extraPadding.str.toAnnotatedString() }
ClickableLinkText(
text = styledText,
linkAnnotationTag = "URL",
@@ -110,6 +127,10 @@ internal fun TimelineItemTextViewDarkPreview(@PreviewParameter(TimelineItemTextB
@Composable
fun ContentToPreview(content: TimelineItemTextBasedContent) {
TimelineItemTextView(content, MutableInteractionSource())
TimelineItemTextView(
content = content,
interactionSource = MutableInteractionSource(),
extraPadding = ExtraPadding("xxxxxxx"),
)
}

View File

@@ -30,12 +30,14 @@ import io.element.android.libraries.ui.strings.CommonStrings
@Composable
fun TimelineItemUnknownView(
content: TimelineItemUnknownContent,
extraPadding: ExtraPadding,
modifier: Modifier = Modifier
) {
TimelineItemInformativeView(
text = stringResource(id = CommonStrings.common_unsupported_event) + extraPaddingTrick,
text = stringResource(id = CommonStrings.common_unsupported_event),
iconDescription = stringResource(id = CommonStrings.dialog_title_warning),
icon = Icons.Default.Info,
extraPadding = extraPadding,
modifier = modifier
)
}
@@ -52,5 +54,8 @@ internal fun TimelineItemUnknownViewDarkPreview() =
@Composable
private fun ContentToPreview() {
TimelineItemUnknownView(TimelineItemUnknownContent)
TimelineItemUnknownView(
content = TimelineItemUnknownContent,
extraPadding = noExtraPadding
)
}

View File

@@ -16,6 +16,38 @@
package io.element.android.features.messages.impl.timeline.components.event
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import io.element.android.features.messages.impl.timeline.model.TimelineItem
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent
import io.element.android.libraries.core.bool.orFalse
import io.element.android.libraries.matrix.api.timeline.item.event.EventSendState
import io.element.android.libraries.ui.strings.R
// Allow to not overlap the timestamp with the text, in the message bubble.
// Compute the size of the worst case.
val extraPaddingTrick: String = " ".repeat(" (edited) 88:88 X ".length)
data class ExtraPadding(val str: String)
val noExtraPadding = ExtraPadding("")
/**
* See [io.element.android.features.messages.impl.timeline.components.TimelineEventTimestampView] for the related View.
* And https://www.figma.com/file/0MMNu7cTOzLOlWb7ctTkv3/Element-X?node-id=1819%253A99506 for the design.
*/
@Composable
fun TimelineItem.Event.toExtraPadding(): ExtraPadding {
val formattedTime = sentTime
val hasMessageSendingFailed = sendState is EventSendState.SendingFailed
val isMessageEdited = (content as? TimelineItemTextBasedContent)?.isEdited.orFalse()
var strLen = 2
if (isMessageEdited) {
strLen += stringResource(id = R.string.common_edited_suffix).length + 2
}
strLen += formattedTime.length
if (hasMessageSendingFailed) {
strLen += 3
}
// A space and a few unbreakable spaces
return ExtraPadding(" " + "\u00A0".repeat(strLen))
}