diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt index e9588ea55f..8eafc77e2c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt @@ -55,8 +55,8 @@ import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction -import io.element.android.features.messages.impl.sender.SenderName -import io.element.android.features.messages.impl.sender.SenderNameMode +import io.element.android.libraries.matrix.ui.messages.sender.SenderName +import io.element.android.libraries.matrix.ui.messages.sender.SenderNameMode import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemCallNotifyContent diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt index a15763648d..19df7b0d68 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt @@ -18,7 +18,6 @@ package io.element.android.features.messages.impl.timeline import io.element.android.features.messages.impl.timeline.components.aProfileTimelineDetailsReady import io.element.android.features.messages.impl.timeline.components.receipt.aReadReceiptData -import io.element.android.features.messages.impl.timeline.model.InReplyToDetails import io.element.android.features.messages.impl.timeline.model.NewEventState import io.element.android.features.messages.impl.timeline.model.ReadReceiptData import io.element.android.features.messages.impl.timeline.model.TimelineItem @@ -37,6 +36,7 @@ import io.element.android.libraries.matrix.api.core.TransactionId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState +import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt index 0c5dec2553..bc2197c2cf 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt @@ -26,7 +26,6 @@ import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.absoluteOffset @@ -56,9 +55,6 @@ import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.invisibleToUser import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.testTag -import androidx.compose.ui.text.font.FontStyle -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.IntOffset @@ -68,8 +64,8 @@ import androidx.constraintlayout.compose.ConstrainScope import androidx.constraintlayout.compose.ConstraintLayout import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons -import io.element.android.features.messages.impl.sender.SenderName -import io.element.android.features.messages.impl.sender.SenderNameMode +import io.element.android.libraries.matrix.ui.messages.sender.SenderName +import io.element.android.libraries.matrix.ui.messages.sender.SenderNameMode import io.element.android.features.messages.impl.timeline.TimelineEvents import io.element.android.features.messages.impl.timeline.TimelineRoomInfo import io.element.android.features.messages.impl.timeline.aTimelineItemEvent @@ -78,8 +74,6 @@ import io.element.android.features.messages.impl.timeline.components.layout.Cont import io.element.android.features.messages.impl.timeline.components.layout.ContentAvoidingLayoutData import io.element.android.features.messages.impl.timeline.components.receipt.ReadReceiptViewState import io.element.android.features.messages.impl.timeline.components.receipt.TimelineItemReadReceiptView -import io.element.android.features.messages.impl.timeline.model.InReplyToDetails -import io.element.android.features.messages.impl.timeline.model.InReplyToMetadata 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.bubble.BubbleState @@ -93,14 +87,10 @@ import io.element.android.features.messages.impl.timeline.model.event.aTimelineI import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent import io.element.android.features.messages.impl.timeline.model.event.canBeRepliedTo import io.element.android.features.messages.impl.timeline.model.event.isEdited -import io.element.android.features.messages.impl.timeline.model.eventId -import io.element.android.features.messages.impl.timeline.model.metadata -import io.element.android.libraries.designsystem.atomic.atoms.PlaceholderAtom import io.element.android.libraries.designsystem.colors.AvatarColorsProvider import io.element.android.libraries.designsystem.components.EqualWidthColumn import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.AvatarData -import io.element.android.libraries.designsystem.icons.CompoundDrawables import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.swipe.SwipeableActionsState @@ -111,8 +101,9 @@ import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails -import io.element.android.libraries.matrix.api.timeline.item.event.getDisambiguatedDisplayName -import io.element.android.libraries.matrix.ui.components.AttachmentThumbnail +import io.element.android.libraries.matrix.ui.messages.reply.InReplyToBox +import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails +import io.element.android.libraries.matrix.ui.messages.reply.eventId import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.coroutines.launch @@ -560,25 +551,7 @@ private fun MessageEventBubbleContent( .clip(RoundedCornerShape(6.dp)) // FIXME when a node is clickable, its contents won't be added to the semantics tree of its parent .clickable(onClick = inReplyToClick) - when (inReplyTo) { - is InReplyToDetails.Ready -> { - ReplyToContent( - senderId = inReplyTo.senderId, - senderProfile = inReplyTo.senderProfile, - metadata = inReplyTo.metadata(), - modifier = inReplyToModifier, - ) - } - is InReplyToDetails.Error -> - ReplyToErrorContent( - data = inReplyTo, - modifier = inReplyToModifier, - ) - is InReplyToDetails.Loading -> - ReplyToLoadingContent( - modifier = inReplyToModifier, - ) - } + InReplyToBox(inReplyTo, modifier = inReplyToModifier) } if (inReplyToDetails != null) { // Use SubComposeLayout only if necessary as it can have consequences on the performance. @@ -614,128 +587,6 @@ private fun MessageEventBubbleContent( ) } -@Composable -private fun ReplyToContent( - senderId: UserId, - senderProfile: ProfileTimelineDetails, - metadata: InReplyToMetadata?, - modifier: Modifier = Modifier, -) { - val paddings = if (metadata is InReplyToMetadata.Thumbnail) { - PaddingValues(start = 4.dp, end = 12.dp, top = 4.dp, bottom = 4.dp) - } else { - PaddingValues(horizontal = 12.dp, vertical = 4.dp) - } - Row( - modifier - .background(MaterialTheme.colorScheme.surface) - .padding(paddings) - ) { - if (metadata is InReplyToMetadata.Thumbnail) { - AttachmentThumbnail( - info = metadata.attachmentThumbnailInfo, - backgroundColor = MaterialTheme.colorScheme.surfaceVariant, - modifier = Modifier - .size(36.dp) - .clip(RoundedCornerShape(4.dp)) - ) - Spacer(modifier = Modifier.width(8.dp)) - } - val a11InReplyToText = stringResource(CommonStrings.common_in_reply_to, senderProfile.getDisambiguatedDisplayName(senderId)) - Column(verticalArrangement = Arrangement.SpaceBetween) { - SenderName( - senderId = senderId, - senderProfile = senderProfile, - senderNameMode = SenderNameMode.Reply, - modifier = Modifier.semantics { - contentDescription = a11InReplyToText - }, - ) - ReplyToContentText(metadata) - } - } -} - -@Composable -private fun ReplyToLoadingContent( - modifier: Modifier = Modifier, -) { - val paddings = PaddingValues(horizontal = 12.dp, vertical = 4.dp) - Row( - modifier - .background(MaterialTheme.colorScheme.surface) - .padding(paddings) - ) { - Column(verticalArrangement = Arrangement.spacedBy(4.dp)) { - PlaceholderAtom(width = 80.dp, height = 12.dp) - PlaceholderAtom(width = 140.dp, height = 14.dp) - } - } -} - -@Composable -private fun ReplyToErrorContent( - data: InReplyToDetails.Error, - modifier: Modifier = Modifier, -) { - val paddings = PaddingValues(horizontal = 12.dp, vertical = 4.dp) - Row( - modifier - .background(MaterialTheme.colorScheme.surface) - .padding(paddings) - ) { - Text( - text = data.message, - style = ElementTheme.typography.fontBodyMdRegular, - color = MaterialTheme.colorScheme.error, - maxLines = 2, - overflow = TextOverflow.Ellipsis, - ) - } -} - -@Composable -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 - null -> "" - } - val iconResourceId = when (metadata) { - InReplyToMetadata.Redacted -> CompoundDrawables.ic_compound_delete - InReplyToMetadata.UnableToDecrypt -> CompoundDrawables.ic_compound_time - else -> null - } - val fontStyle = when (metadata) { - is InReplyToMetadata.Informative -> FontStyle.Italic - else -> FontStyle.Normal - } - Row( - verticalAlignment = Alignment.CenterVertically, - ) { - if (iconResourceId != null) { - Icon( - resourceId = iconResourceId, - tint = MaterialTheme.colorScheme.secondary, - contentDescription = null, - modifier = Modifier.size(16.dp) - ) - Spacer(modifier = Modifier.width(4.dp)) - } - Text( - text = text, - style = ElementTheme.typography.fontBodyMdRegular, - fontStyle = fontStyle, - textAlign = TextAlign.Start, - color = MaterialTheme.colorScheme.secondary, - maxLines = 2, - overflow = TextOverflow.Ellipsis, - ) - } -} - @PreviewsDayNight @Composable internal fun TimelineItemEventRowPreview() = ElementPreview { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowDisambiguatedPreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowDisambiguatedPreview.kt index 2527072c24..e449f0beda 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowDisambiguatedPreview.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowDisambiguatedPreview.kt @@ -18,10 +18,10 @@ package io.element.android.features.messages.impl.timeline.components import androidx.compose.runtime.Composable import androidx.compose.ui.tooling.preview.PreviewParameter -import io.element.android.features.messages.impl.timeline.model.InReplyToDetails import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType +import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails @PreviewsDayNight @Composable diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyInformativePreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyInformativePreview.kt index 2dbe3eefa4..7a97ab935a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyInformativePreview.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyInformativePreview.kt @@ -18,11 +18,11 @@ package io.element.android.features.messages.impl.timeline.components import androidx.compose.runtime.Composable import androidx.compose.ui.tooling.preview.PreviewParameter -import io.element.android.features.messages.impl.timeline.model.InReplyToDetails import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecryptContent +import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails @PreviewsDayNight @Composable diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyOtherPreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyOtherPreview.kt index 2231af606d..bd31103ff0 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyOtherPreview.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyOtherPreview.kt @@ -18,10 +18,10 @@ package io.element.android.features.messages.impl.timeline.components import androidx.compose.runtime.Composable import androidx.compose.ui.tooling.preview.PreviewParameter -import io.element.android.features.messages.impl.timeline.model.InReplyToDetails import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails @PreviewsDayNight @Composable diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyPreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyPreview.kt index b1500dac2c..e5e1daca8a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyPreview.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyPreview.kt @@ -22,7 +22,6 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider 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.model.InReplyToDetails import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent @@ -47,6 +46,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.StickerMessag import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageType +import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentMapOf diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt index 50550bd84f..aea1019f11 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt @@ -24,7 +24,6 @@ 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.TimelineItemReactions import io.element.android.features.messages.impl.timeline.model.TimelineItemReadReceipts -import io.element.android.features.messages.impl.timeline.model.map import io.element.android.libraries.core.bool.orTrue import io.element.android.libraries.dateformatter.api.LastMessageTimestampFormatter import io.element.android.libraries.designsystem.components.avatar.AvatarData @@ -35,6 +34,7 @@ import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.matrix.api.timeline.item.event.getAvatarUrl import io.element.android.libraries.matrix.api.timeline.item.event.getDisambiguatedDisplayName +import io.element.android.libraries.matrix.ui.messages.reply.map import kotlinx.collections.immutable.toImmutableList import java.text.DateFormat import java.util.Date diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt index e948484e5e..f77db70506 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt @@ -30,6 +30,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSen import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails import io.element.android.libraries.matrix.api.timeline.item.event.TimelineItemEventOrigin import io.element.android.libraries.matrix.api.timeline.item.event.getDisambiguatedDisplayName +import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails import kotlinx.collections.immutable.ImmutableList @Immutable diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToBox.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToBox.kt new file mode 100644 index 0000000000..631f0e1c7d --- /dev/null +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToBox.kt @@ -0,0 +1,197 @@ +/* + * 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 + * + * http://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.libraries.matrix.ui.messages.reply + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.libraries.designsystem.atomic.atoms.PlaceholderAtom +import io.element.android.libraries.designsystem.icons.CompoundDrawables +import io.element.android.libraries.designsystem.theme.components.Icon +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails +import io.element.android.libraries.matrix.api.timeline.item.event.getDisambiguatedDisplayName +import io.element.android.libraries.matrix.ui.components.AttachmentThumbnail +import io.element.android.libraries.matrix.ui.messages.sender.SenderName +import io.element.android.libraries.matrix.ui.messages.sender.SenderNameMode +import io.element.android.libraries.ui.strings.CommonStrings + +@Composable +fun InReplyToBox( + inReplyTo: InReplyToDetails, + modifier: Modifier = Modifier, +) { + Box(modifier = modifier) { + when (inReplyTo) { + is InReplyToDetails.Ready -> { + ReplyToReadyContent( + senderId = inReplyTo.senderId, + senderProfile = inReplyTo.senderProfile, + metadata = inReplyTo.metadata(), + ) + } + is InReplyToDetails.Error -> + ReplyToErrorContent(data = inReplyTo) + is InReplyToDetails.Loading -> + ReplyToLoadingContent() + } + } +} + +@Composable +private fun ReplyToReadyContent( + senderId: UserId, + senderProfile: ProfileTimelineDetails, + metadata: InReplyToMetadata?, + modifier: Modifier = Modifier, +) { + val paddings = if (metadata is InReplyToMetadata.Thumbnail) { + PaddingValues(start = 4.dp, end = 12.dp, top = 4.dp, bottom = 4.dp) + } else { + PaddingValues(horizontal = 12.dp, vertical = 4.dp) + } + Row( + modifier + .background(MaterialTheme.colorScheme.surface) + .padding(paddings) + ) { + if (metadata is InReplyToMetadata.Thumbnail) { + AttachmentThumbnail( + info = metadata.attachmentThumbnailInfo, + backgroundColor = MaterialTheme.colorScheme.surfaceVariant, + modifier = Modifier + .size(36.dp) + .clip(RoundedCornerShape(4.dp)) + ) + Spacer(modifier = Modifier.width(8.dp)) + } + val a11InReplyToText = stringResource(CommonStrings.common_in_reply_to, senderProfile.getDisambiguatedDisplayName(senderId)) + Column(verticalArrangement = Arrangement.SpaceBetween) { + SenderName( + senderId = senderId, + senderProfile = senderProfile, + senderNameMode = SenderNameMode.Reply, + modifier = Modifier.semantics { + contentDescription = a11InReplyToText + }, + ) + ReplyToContentText(metadata) + } + } +} + +@Composable +private fun ReplyToLoadingContent( + modifier: Modifier = Modifier, +) { + val paddings = PaddingValues(horizontal = 12.dp, vertical = 4.dp) + Row( + modifier + .background(MaterialTheme.colorScheme.surface) + .padding(paddings) + ) { + Column(verticalArrangement = Arrangement.spacedBy(4.dp)) { + PlaceholderAtom(width = 80.dp, height = 12.dp) + PlaceholderAtom(width = 140.dp, height = 14.dp) + } + } +} + +@Composable +private fun ReplyToErrorContent( + data: InReplyToDetails.Error, + modifier: Modifier = Modifier, +) { + val paddings = PaddingValues(horizontal = 12.dp, vertical = 4.dp) + Row( + modifier + .background(MaterialTheme.colorScheme.surface) + .padding(paddings) + ) { + Text( + text = data.message, + style = ElementTheme.typography.fontBodyMdRegular, + color = MaterialTheme.colorScheme.error, + maxLines = 2, + overflow = TextOverflow.Ellipsis, + ) + } +} + +@Composable +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 + null -> "" + } + val iconResourceId = when (metadata) { + InReplyToMetadata.Redacted -> CompoundDrawables.ic_compound_delete + InReplyToMetadata.UnableToDecrypt -> CompoundDrawables.ic_compound_time + else -> null + } + val fontStyle = when (metadata) { + is InReplyToMetadata.Informative -> FontStyle.Italic + else -> FontStyle.Normal + } + Row( + verticalAlignment = Alignment.CenterVertically, + ) { + if (iconResourceId != null) { + Icon( + resourceId = iconResourceId, + tint = MaterialTheme.colorScheme.secondary, + contentDescription = null, + modifier = Modifier.size(16.dp) + ) + Spacer(modifier = Modifier.width(4.dp)) + } + Text( + text = text, + style = ElementTheme.typography.fontBodyMdRegular, + fontStyle = fontStyle, + textAlign = TextAlign.Start, + color = MaterialTheme.colorScheme.secondary, + maxLines = 2, + overflow = TextOverflow.Ellipsis, + ) + } +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/InReplyToDetails.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToDetails.kt similarity index 96% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/InReplyToDetails.kt rename to libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToDetails.kt index 5317c3231e..18202e5df2 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/InReplyToDetails.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToDetails.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 New Vector Ltd + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.messages.impl.timeline.model +package io.element.android.libraries.matrix.ui.messages.reply import androidx.compose.runtime.Immutable import io.element.android.libraries.matrix.api.core.EventId diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/InReplyToMetadata.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadata.kt similarity index 98% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/InReplyToMetadata.kt rename to libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadata.kt index ab2e418587..689d38e3b7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/InReplyToMetadata.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadata.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 New Vector Ltd + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.messages.impl.timeline.model +package io.element.android.libraries.matrix.ui.messages.reply import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/sender/SenderName.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/sender/SenderName.kt similarity index 98% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/sender/SenderName.kt rename to libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/sender/SenderName.kt index 9daa95198e..5ff84ff292 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/sender/SenderName.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/sender/SenderName.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.messages.impl.sender +package io.element.android.libraries.matrix.ui.messages.sender import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/sender/SenderNameDataProvider.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/sender/SenderNameDataProvider.kt similarity index 91% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/sender/SenderNameDataProvider.kt rename to libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/sender/SenderNameDataProvider.kt index 138038bb2f..1f5caefcf0 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/sender/SenderNameDataProvider.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/sender/SenderNameDataProvider.kt @@ -14,11 +14,10 @@ * limitations under the License. */ -package io.element.android.features.messages.impl.sender +package io.element.android.libraries.matrix.ui.messages.sender import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import io.element.android.features.messages.impl.timeline.components.aProfileTimelineDetailsReady import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails @@ -58,9 +57,10 @@ private fun aSenderNameData( displayNameAmbiguous: Boolean = false, ) = SenderNameData( userId = UserId("@alice:${senderNameMode.javaClass.simpleName.lowercase()}"), - profileTimelineDetails = aProfileTimelineDetailsReady( + profileTimelineDetails = ProfileTimelineDetails.Ready( displayName = "Alice ${senderNameMode.javaClass.simpleName}", displayNameAmbiguous = displayNameAmbiguous, + avatarUrl = null ), senderNameMode = senderNameMode, ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/sender/SenderNameMode.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/sender/SenderNameMode.kt similarity index 93% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/sender/SenderNameMode.kt rename to libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/sender/SenderNameMode.kt index c61033500a..99190d17b9 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/sender/SenderNameMode.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/sender/SenderNameMode.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.messages.impl.sender +package io.element.android.libraries.matrix.ui.messages.sender import androidx.compose.runtime.Immutable import androidx.compose.ui.graphics.Color