Browse Source

Reply : move InReplyTo and related to matrix.ui module.

pull/3099/head
ganfra 3 months ago
parent
commit
08816bd2df
  1. 4
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt
  2. 2
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt
  3. 161
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt
  4. 2
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowDisambiguatedPreview.kt
  5. 2
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyInformativePreview.kt
  6. 2
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyOtherPreview.kt
  7. 2
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyPreview.kt
  8. 2
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt
  9. 1
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt
  10. 197
      libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToBox.kt
  11. 4
      libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToDetails.kt
  12. 4
      libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadata.kt
  13. 2
      libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/sender/SenderName.kt
  14. 6
      libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/sender/SenderNameDataProvider.kt
  15. 2
      libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/sender/SenderNameMode.kt

4
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.theme.ElementTheme
import io.element.android.compound.tokens.generated.CompoundIcons 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.actionlist.model.TimelineItemAction
import io.element.android.features.messages.impl.sender.SenderName import io.element.android.libraries.matrix.ui.messages.sender.SenderName
import io.element.android.features.messages.impl.sender.SenderNameMode 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.TimelineItem
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemCallNotifyContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemCallNotifyContent

2
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.aProfileTimelineDetailsReady
import io.element.android.features.messages.impl.timeline.components.receipt.aReadReceiptData 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.NewEventState
import io.element.android.features.messages.impl.timeline.model.ReadReceiptData import io.element.android.features.messages.impl.timeline.model.ReadReceiptData
import io.element.android.features.messages.impl.timeline.model.TimelineItem 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.core.UserId
import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo 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.api.timeline.item.event.LocalEventSendState
import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList

161
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.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.absoluteOffset 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.invisibleToUser
import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.testTag 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.Dp
import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntOffset
@ -68,8 +64,8 @@ import androidx.constraintlayout.compose.ConstrainScope
import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.ConstraintLayout
import io.element.android.compound.theme.ElementTheme import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.features.messages.impl.sender.SenderName import io.element.android.libraries.matrix.ui.messages.sender.SenderName
import io.element.android.features.messages.impl.sender.SenderNameMode 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.TimelineEvents
import io.element.android.features.messages.impl.timeline.TimelineRoomInfo import io.element.android.features.messages.impl.timeline.TimelineRoomInfo
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent 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.layout.ContentAvoidingLayoutData
import io.element.android.features.messages.impl.timeline.components.receipt.ReadReceiptViewState 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.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.TimelineItem
import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition
import io.element.android.features.messages.impl.timeline.model.bubble.BubbleState 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.aTimelineItemTextContent
import io.element.android.features.messages.impl.timeline.model.event.canBeRepliedTo 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.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.colors.AvatarColorsProvider
import io.element.android.libraries.designsystem.components.EqualWidthColumn 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.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData 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.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.swipe.SwipeableActionsState 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.EventId
import io.element.android.libraries.matrix.api.core.UserId 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.ProfileTimelineDetails
import io.element.android.libraries.matrix.api.timeline.item.event.getDisambiguatedDisplayName import io.element.android.libraries.matrix.ui.messages.reply.InReplyToBox
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnail 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.testtags.TestTags
import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -560,25 +551,7 @@ private fun MessageEventBubbleContent(
.clip(RoundedCornerShape(6.dp)) .clip(RoundedCornerShape(6.dp))
// FIXME when a node is clickable, its contents won't be added to the semantics tree of its parent // FIXME when a node is clickable, its contents won't be added to the semantics tree of its parent
.clickable(onClick = inReplyToClick) .clickable(onClick = inReplyToClick)
when (inReplyTo) { InReplyToBox(inReplyTo, modifier = inReplyToModifier)
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,
)
}
} }
if (inReplyToDetails != null) { if (inReplyToDetails != null) {
// Use SubComposeLayout only if necessary as it can have consequences on the performance. // 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 @PreviewsDayNight
@Composable @Composable
internal fun TimelineItemEventRowPreview() = ElementPreview { internal fun TimelineItemEventRowPreview() = ElementPreview {

2
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.runtime.Composable
import androidx.compose.ui.tooling.preview.PreviewParameter 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.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight 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.api.timeline.item.event.TextMessageType
import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails
@PreviewsDayNight @PreviewsDayNight
@Composable @Composable

2
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.runtime.Composable
import androidx.compose.ui.tooling.preview.PreviewParameter 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.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight 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.RedactedContent
import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecryptContent import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecryptContent
import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails
@PreviewsDayNight @PreviewsDayNight
@Composable @Composable

2
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.runtime.Composable
import androidx.compose.ui.tooling.preview.PreviewParameter 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.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails
@PreviewsDayNight @PreviewsDayNight
@Composable @Composable

2
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 androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent 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.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.TimelineItemGroupPosition
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent 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.TextMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType 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.api.timeline.item.event.VoiceMessageType
import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.persistentMapOf import kotlinx.collections.immutable.persistentMapOf

2
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.TimelineItemGroupPosition
import io.element.android.features.messages.impl.timeline.model.TimelineItemReactions 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.TimelineItemReadReceipts
import io.element.android.features.messages.impl.timeline.model.map
import io.element.android.libraries.core.bool.orTrue import io.element.android.libraries.core.bool.orTrue
import io.element.android.libraries.dateformatter.api.LastMessageTimestampFormatter import io.element.android.libraries.dateformatter.api.LastMessageTimestampFormatter
import io.element.android.libraries.designsystem.components.avatar.AvatarData 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.MatrixTimelineItem
import io.element.android.libraries.matrix.api.timeline.item.event.getAvatarUrl 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.api.timeline.item.event.getDisambiguatedDisplayName
import io.element.android.libraries.matrix.ui.messages.reply.map
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import java.text.DateFormat import java.text.DateFormat
import java.util.Date import java.util.Date

1
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.ProfileTimelineDetails
import io.element.android.libraries.matrix.api.timeline.item.event.TimelineItemEventOrigin 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.api.timeline.item.event.getDisambiguatedDisplayName
import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
@Immutable @Immutable

197
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,
)
}
}

4
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/InReplyToDetails.kt → 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -14,7 +14,7 @@
* limitations under the License. * 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 androidx.compose.runtime.Immutable
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId

4
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/InReplyToMetadata.kt → 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -14,7 +14,7 @@
* limitations under the License. * 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.Composable
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable

2
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/sender/SenderName.kt → libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/sender/SenderName.kt

@ -14,7 +14,7 @@
* limitations under the License. * 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.Arrangement
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row

6
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/sender/SenderNameDataProvider.kt → libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/sender/SenderNameDataProvider.kt

@ -14,11 +14,10 @@
* limitations under the License. * 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.graphics.Color
import androidx.compose.ui.tooling.preview.PreviewParameterProvider 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.core.UserId
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails
@ -58,9 +57,10 @@ private fun aSenderNameData(
displayNameAmbiguous: Boolean = false, displayNameAmbiguous: Boolean = false,
) = SenderNameData( ) = SenderNameData(
userId = UserId("@alice:${senderNameMode.javaClass.simpleName.lowercase()}"), userId = UserId("@alice:${senderNameMode.javaClass.simpleName.lowercase()}"),
profileTimelineDetails = aProfileTimelineDetailsReady( profileTimelineDetails = ProfileTimelineDetails.Ready(
displayName = "Alice ${senderNameMode.javaClass.simpleName}", displayName = "Alice ${senderNameMode.javaClass.simpleName}",
displayNameAmbiguous = displayNameAmbiguous, displayNameAmbiguous = displayNameAmbiguous,
avatarUrl = null
), ),
senderNameMode = senderNameMode, senderNameMode = senderNameMode,
) )

2
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/sender/SenderNameMode.kt → libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/sender/SenderNameMode.kt

@ -14,7 +14,7 @@
* limitations under the License. * 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.runtime.Immutable
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
Loading…
Cancel
Save