Skip to content

Commit 58a4cc2

Browse files
authored
Merge pull request #2384 from element-hq/feature/bma/replyOnRedactedRendering
Reply on redacted and UTD rendering
2 parents ad09a60 + 7bb0ec6 commit 58a4cc2

File tree

224 files changed

+307
-29
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

224 files changed

+307
-29
lines changed

changelog.d/2318.misc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Render correctly in reply to data when Event cannot be decrypted or has been redacted

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import androidx.compose.ui.platform.LocalContext
5353
import androidx.compose.ui.platform.LocalViewConfiguration
5454
import androidx.compose.ui.platform.ViewConfiguration
5555
import androidx.compose.ui.res.stringResource
56+
import androidx.compose.ui.text.font.FontStyle
5657
import androidx.compose.ui.text.style.TextAlign
5758
import androidx.compose.ui.text.style.TextOverflow
5859
import androidx.compose.ui.unit.Dp
@@ -92,6 +93,7 @@ import io.element.android.libraries.designsystem.colors.AvatarColorsProvider
9293
import io.element.android.libraries.designsystem.components.EqualWidthColumn
9394
import io.element.android.libraries.designsystem.components.avatar.Avatar
9495
import io.element.android.libraries.designsystem.components.avatar.AvatarData
96+
import io.element.android.libraries.designsystem.icons.CompoundDrawables
9597
import io.element.android.libraries.designsystem.preview.ElementPreview
9698
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
9799
import io.element.android.libraries.designsystem.swipe.SwipeableActionsState
@@ -648,15 +650,50 @@ private fun ReplyToContent(
648650
maxLines = 1,
649651
overflow = TextOverflow.Ellipsis,
650652
)
651-
Text(
652-
text = metadata?.text.orEmpty(),
653-
style = ElementTheme.typography.fontBodyMdRegular,
654-
textAlign = TextAlign.Start,
655-
color = ElementTheme.materialColors.secondary,
656-
maxLines = 2,
657-
overflow = TextOverflow.Ellipsis,
653+
ReplyToContentText(metadata)
654+
}
655+
}
656+
}
657+
658+
@Composable
659+
private fun ReplyToContentText(metadata: InReplyToMetadata?) {
660+
val text = when (metadata) {
661+
InReplyToMetadata.Redacted -> stringResource(id = CommonStrings.common_message_removed)
662+
InReplyToMetadata.UnableToDecrypt -> stringResource(id = CommonStrings.common_waiting_for_decryption_key)
663+
is InReplyToMetadata.Text -> metadata.text
664+
is InReplyToMetadata.Thumbnail -> metadata.text
665+
null -> ""
666+
}
667+
val iconResourceId = when (metadata) {
668+
InReplyToMetadata.Redacted -> CompoundDrawables.ic_compound_delete
669+
InReplyToMetadata.UnableToDecrypt -> CompoundDrawables.ic_compound_time
670+
else -> null
671+
}
672+
val fontStyle = when (metadata) {
673+
is InReplyToMetadata.Informative -> FontStyle.Italic
674+
else -> FontStyle.Normal
675+
}
676+
Row(
677+
verticalAlignment = Alignment.CenterVertically,
678+
) {
679+
if (iconResourceId != null) {
680+
Icon(
681+
resourceId = iconResourceId,
682+
tint = MaterialTheme.colorScheme.secondary,
683+
contentDescription = null,
684+
modifier = Modifier.size(16.dp)
658685
)
686+
Spacer(modifier = Modifier.width(4.dp))
659687
}
688+
Text(
689+
text = text,
690+
style = ElementTheme.typography.fontBodyMdRegular,
691+
fontStyle = fontStyle,
692+
textAlign = TextAlign.Start,
693+
color = MaterialTheme.colorScheme.secondary,
694+
maxLines = 2,
695+
overflow = TextOverflow.Ellipsis,
696+
)
660697
}
661698
}
662699

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright (c) 2023 New Vector Ltd
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.element.android.features.messages.impl.timeline.components
18+
19+
import androidx.compose.runtime.Composable
20+
import androidx.compose.ui.tooling.preview.PreviewParameter
21+
import io.element.android.features.messages.impl.timeline.model.InReplyToDetails
22+
import io.element.android.libraries.designsystem.preview.ElementPreview
23+
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
24+
import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent
25+
import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecryptContent
26+
27+
@PreviewsDayNight
28+
@Composable
29+
internal fun TimelineItemEventRowWithReplyInformativePreview(
30+
@PreviewParameter(InReplyToDetailsInformativeProvider::class) inReplyToDetails: InReplyToDetails,
31+
) = ElementPreview {
32+
TimelineItemEventRowWithReplyContentToPreview(inReplyToDetails)
33+
}
34+
35+
class InReplyToDetailsInformativeProvider : InReplyToDetailsProvider() {
36+
override val values: Sequence<InReplyToDetails>
37+
get() = sequenceOf(
38+
RedactedContent,
39+
UnableToDecryptContent(UnableToDecryptContent.Data.Unknown),
40+
).map {
41+
aInReplyToDetails(
42+
eventContent = it,
43+
)
44+
}
45+
}

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyPreview.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ import kotlinx.collections.immutable.persistentMapOf
5454
internal fun TimelineItemEventRowWithReplyPreview(
5555
@PreviewParameter(InReplyToDetailsProvider::class) inReplyToDetails: InReplyToDetails,
5656
) = ElementPreview {
57+
TimelineItemEventRowWithReplyContentToPreview(inReplyToDetails)
58+
}
59+
60+
@Composable
61+
internal fun TimelineItemEventRowWithReplyContentToPreview(inReplyToDetails: InReplyToDetails) {
5762
Column {
5863
sequenceOf(false, true).forEach {
5964
ATimelineItemEventRow(
@@ -83,7 +88,7 @@ internal fun TimelineItemEventRowWithReplyPreview(
8388
}
8489
}
8590

86-
class InReplyToDetailsProvider : PreviewParameterProvider<InReplyToDetails> {
91+
open class InReplyToDetailsProvider : PreviewParameterProvider<InReplyToDetails> {
8792
override val values: Sequence<InReplyToDetails>
8893
get() = sequenceOf(
8994
aMessageContent(
@@ -156,7 +161,7 @@ class InReplyToDetailsProvider : PreviewParameterProvider<InReplyToDetails> {
156161
type = type,
157162
)
158163

159-
private fun aInReplyToDetails(
164+
protected fun aInReplyToDetails(
160165
eventContent: EventContent,
161166
) = InReplyToDetails(
162167
eventId = EventId("\$event"),

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/InReplyToMetadata.kt

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,20 @@ import androidx.compose.runtime.Immutable
2121
import androidx.compose.ui.res.stringResource
2222
import io.element.android.libraries.matrix.api.media.MediaSource
2323
import io.element.android.libraries.matrix.api.timeline.item.event.AudioMessageType
24+
import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseMessageLikeContent
25+
import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseStateContent
2426
import io.element.android.libraries.matrix.api.timeline.item.event.FileMessageType
2527
import io.element.android.libraries.matrix.api.timeline.item.event.ImageMessageType
2628
import io.element.android.libraries.matrix.api.timeline.item.event.LocationMessageType
2729
import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent
2830
import io.element.android.libraries.matrix.api.timeline.item.event.PollContent
31+
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent
32+
import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent
33+
import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent
34+
import io.element.android.libraries.matrix.api.timeline.item.event.StateContent
2935
import io.element.android.libraries.matrix.api.timeline.item.event.StickerContent
36+
import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecryptContent
37+
import io.element.android.libraries.matrix.api.timeline.item.event.UnknownContent
3038
import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType
3139
import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageType
3240
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailInfo
@@ -35,17 +43,20 @@ import io.element.android.libraries.ui.strings.CommonStrings
3543

3644
@Immutable
3745
internal sealed interface InReplyToMetadata {
38-
val text: String?
39-
4046
data class Thumbnail(
4147
val attachmentThumbnailInfo: AttachmentThumbnailInfo
4248
) : InReplyToMetadata {
43-
override val text: String? = attachmentThumbnailInfo.textContent
49+
val text: String = attachmentThumbnailInfo.textContent.orEmpty()
4450
}
4551

4652
data class Text(
47-
override val text: String
53+
val text: String
4854
) : InReplyToMetadata
55+
56+
sealed interface Informative : InReplyToMetadata
57+
58+
data object Redacted : Informative
59+
data object UnableToDecrypt : Informative
4960
}
5061

5162
/**
@@ -103,7 +114,8 @@ internal fun InReplyToDetails.metadata(): InReplyToMetadata? = when (eventConten
103114
AttachmentThumbnailInfo(
104115
thumbnailSource = MediaSource(eventContent.url),
105116
textContent = eventContent.body,
106-
type = AttachmentThumbnailType.Image
117+
type = AttachmentThumbnailType.Image,
118+
blurHash = eventContent.info.blurhash,
107119
)
108120
)
109121
is PollContent -> InReplyToMetadata.Thumbnail(
@@ -112,5 +124,13 @@ internal fun InReplyToDetails.metadata(): InReplyToMetadata? = when (eventConten
112124
type = AttachmentThumbnailType.Poll,
113125
)
114126
)
115-
else -> null
127+
is RedactedContent -> InReplyToMetadata.Redacted
128+
is UnableToDecryptContent -> InReplyToMetadata.UnableToDecrypt
129+
is FailedToParseMessageLikeContent,
130+
is FailedToParseStateContent,
131+
is ProfileChangeContent,
132+
is RoomMembershipContent,
133+
is StateContent,
134+
UnknownContent,
135+
null -> null
116136
}

0 commit comments

Comments
 (0)