Browse Source

Format state Event: use disambiguated Display name #2722.

- Rename some parameter and val from `senderDisplayName` (and consort) to `senderDisambiguatedDisplayName`.
- In `InReplyToDetails`, replace `senderDisplayName` and `senderAvatarUrl` by `senderProfile`.
pull/2739/head
Benoit Marty 5 months ago committed by Benoit Marty
parent
commit
03abfcaadb
  1. 4
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt
  2. 4
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt
  3. 11
      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/TimelineItemEventRowLongSenderNamePreview.kt
  5. 2
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowTimestampPreview.kt
  6. 8
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithReplyPreview.kt
  7. 8
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt
  8. 12
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt
  9. 14
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt
  10. 7
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/InReplyToDetails.kt
  11. 4
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt
  12. 2
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/MessageEventFixtures.kt
  13. 44
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactoryTest.kt
  14. 2
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouperTest.kt
  15. 10
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/model/InReplyToDetailTest.kt
  16. 8
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/model/InReplyToMetadataKtTest.kt
  17. 44
      libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt
  18. 8
      libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultTimelineEventFormatter.kt
  19. 11
      libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/ProfileChangeContentFormatter.kt
  20. 22
      libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/RoomMembershipContentFormatter.kt
  21. 18
      libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/StateContentFormatter.kt
  22. 5
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt
  23. 3
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/InReplyTo.kt
  24. 5
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt
  25. 18
      libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt
  26. 12
      libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt
  27. 3
      libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt
  28. 4
      libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolverTest.kt
  29. 2
      libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fixtures/NotifiableEventFixture.kt

4
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt

@ -269,9 +269,9 @@ private fun MessageSummary(event: TimelineItem.Event, modifier: Modifier = Modif @@ -269,9 +269,9 @@ private fun MessageSummary(event: TimelineItem.Event, modifier: Modifier = Modif
Spacer(modifier = Modifier.width(8.dp))
Column(modifier = Modifier.weight(1f)) {
Row {
if (event.senderDisplayName != null) {
if (event.senderDisambiguatedDisplayName != null) {
Text(
text = event.senderDisplayName,
text = event.senderDisambiguatedDisplayName,
style = ElementTheme.typography.fontBodySmMedium,
color = MaterialTheme.colorScheme.primary
)

4
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt

@ -130,7 +130,7 @@ internal fun aTimelineItemEvent( @@ -130,7 +130,7 @@ internal fun aTimelineItemEvent(
transactionId: TransactionId? = null,
isMine: Boolean = false,
isEditable: Boolean = false,
senderDisplayName: String = "Sender",
senderDisambiguatedDisplayName: String = "Sender",
content: TimelineItemEventContent = aTimelineItemTextContent(),
groupPosition: TimelineItemGroupPosition = TimelineItemGroupPosition.None,
sendState: LocalEventSendState? = null,
@ -152,7 +152,7 @@ internal fun aTimelineItemEvent( @@ -152,7 +152,7 @@ internal fun aTimelineItemEvent(
sentTime = "12:34",
isMine = isMine,
isEditable = isEditable,
senderDisplayName = senderDisplayName,
senderDisambiguatedDisplayName = senderDisambiguatedDisplayName,
groupPosition = groupPosition,
localSendState = sendState,
inReplyTo = inReplyTo,

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

@ -106,6 +106,7 @@ import io.element.android.libraries.designsystem.theme.components.Icon @@ -106,6 +106,7 @@ 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.EventId
import io.element.android.libraries.matrix.api.core.UserId
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.testtags.TestTags
import io.element.android.libraries.ui.strings.CommonStrings
@ -561,10 +562,10 @@ private fun MessageEventBubbleContent( @@ -561,10 +562,10 @@ private fun MessageEventBubbleContent(
}
}
val inReplyTo = @Composable { inReplyTo: InReplyToDetails ->
val senderName = inReplyTo.senderDisplayName ?: inReplyTo.senderId.value
val senderDisambiguatedDisplayName = inReplyTo.senderProfile.getDisambiguatedDisplayName(inReplyTo.senderId)
val topPadding = if (showThreadDecoration) 0.dp else 8.dp
ReplyToContent(
senderName = senderName,
senderDisambiguatedDisplayName = senderDisambiguatedDisplayName,
metadata = inReplyTo.metadata(),
modifier = Modifier
.padding(top = topPadding, start = 8.dp, end = 8.dp)
@ -609,7 +610,7 @@ private fun MessageEventBubbleContent( @@ -609,7 +610,7 @@ private fun MessageEventBubbleContent(
@Composable
private fun ReplyToContent(
senderName: String,
senderDisambiguatedDisplayName: String,
metadata: InReplyToMetadata?,
modifier: Modifier = Modifier,
) {
@ -633,13 +634,13 @@ private fun ReplyToContent( @@ -633,13 +634,13 @@ private fun ReplyToContent(
)
Spacer(modifier = Modifier.width(8.dp))
}
val a11InReplyToText = stringResource(CommonStrings.common_in_reply_to, senderName)
val a11InReplyToText = stringResource(CommonStrings.common_in_reply_to, senderDisambiguatedDisplayName)
Column(verticalArrangement = Arrangement.SpaceBetween) {
Text(
modifier = Modifier.semantics {
contentDescription = a11InReplyToText
},
text = senderName,
text = senderDisambiguatedDisplayName,
style = ElementTheme.typography.fontBodySmMedium,
textAlign = TextAlign.Start,
color = ElementTheme.materialColors.primary,

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

@ -27,7 +27,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreviewLight @@ -27,7 +27,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreviewLight
internal fun TimelineItemEventRowLongSenderNamePreview() = ElementPreviewLight {
ATimelineItemEventRow(
event = aTimelineItemEvent(
senderDisplayName = "a long sender display name to test single line and ellipsis at the end of the line",
senderDisambiguatedDisplayName = "a long sender display name to test single line and ellipsis at the end of the line",
),
)
}

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

@ -43,7 +43,7 @@ internal fun TimelineItemEventRowTimestampPreview( @@ -43,7 +43,7 @@ internal fun TimelineItemEventRowTimestampPreview(
body = str,
),
reactionsState = aTimelineItemReactions(count = 0),
senderDisplayName = "A sender",
senderDisambiguatedDisplayName = "A sender",
),
)
}

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

@ -42,6 +42,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.MessageConten @@ -42,6 +42,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.MessageConten
import io.element.android.libraries.matrix.api.timeline.item.event.MessageType
import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.PollContent
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails
import io.element.android.libraries.matrix.api.timeline.item.event.StickerMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType
@ -167,8 +168,11 @@ open class InReplyToDetailsProvider : PreviewParameterProvider<InReplyToDetails> @@ -167,8 +168,11 @@ open class InReplyToDetailsProvider : PreviewParameterProvider<InReplyToDetails>
eventId = EventId("\$event"),
eventContent = eventContent,
senderId = UserId("@Sender:domain"),
senderDisplayName = "Sender",
senderAvatarUrl = null,
senderProfile = ProfileTimelineDetails.Ready(
displayName = "Sender",
displayNameAmbiguous = false,
avatarUrl = null,
),
textContent = (eventContent as? MessageContent)?.body.orEmpty(),
)
}

8
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt

@ -52,8 +52,12 @@ class TimelineItemContentFactory @Inject constructor( @@ -52,8 +52,12 @@ class TimelineItemContentFactory @Inject constructor(
is FailedToParseMessageLikeContent -> failedToParseMessageFactory.create(itemContent)
is FailedToParseStateContent -> failedToParseStateFactory.create(itemContent)
is MessageContent -> {
val senderDisplayName = eventTimelineItem.senderProfile.getDisambiguatedDisplayName(eventTimelineItem.sender)
messageFactory.create(itemContent, senderDisplayName, eventTimelineItem.eventId)
val senderDisambiguatedDisplayName = eventTimelineItem.senderProfile.getDisambiguatedDisplayName(eventTimelineItem.sender)
messageFactory.create(
content = itemContent,
senderDisambiguatedDisplayName = senderDisambiguatedDisplayName,
eventId = eventTimelineItem.eventId,
)
}
is ProfileChangeContent -> profileChangeFactory.create(eventTimelineItem)
is RedactedContent -> redactedMessageFactory.create(itemContent)

12
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt

@ -70,17 +70,21 @@ class TimelineItemContentMessageFactory @Inject constructor( @@ -70,17 +70,21 @@ class TimelineItemContentMessageFactory @Inject constructor(
private val htmlConverterProvider: HtmlConverterProvider,
private val permalinkParser: PermalinkParser,
) {
suspend fun create(content: MessageContent, senderDisplayName: String, eventId: EventId?): TimelineItemEventContent {
suspend fun create(
content: MessageContent,
senderDisambiguatedDisplayName: String,
eventId: EventId?,
): TimelineItemEventContent {
return when (val messageType = content.type) {
is EmoteMessageType -> {
val emoteBody = "* $senderDisplayName ${messageType.body.trimEnd()}"
val emoteBody = "* $senderDisambiguatedDisplayName ${messageType.body.trimEnd()}"
TimelineItemEmoteContent(
body = emoteBody,
htmlDocument = messageType.formatted?.toHtmlDocument(
permalinkParser = permalinkParser,
prefix = "* $senderDisplayName",
prefix = "* $senderDisambiguatedDisplayName",
),
formattedBody = parseHtml(messageType.formatted, prefix = "* $senderDisplayName") ?: emoteBody.withLinks(),
formattedBody = parseHtml(messageType.formatted, prefix = "* $senderDisambiguatedDisplayName") ?: emoteBody.withLinks(),
isEdited = content.isEdited,
)
}

14
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt

@ -55,14 +55,14 @@ class TimelineItemEventFactory @Inject constructor( @@ -55,14 +55,14 @@ class TimelineItemEventFactory @Inject constructor(
val currentSender = currentTimelineItem.event.sender
val groupPosition =
computeGroupPosition(currentTimelineItem, timelineItems, index)
val (senderDisplayName, senderAvatarUrl) = currentTimelineItem.getSenderInfo()
val (senderDisambiguatedDisplayName, senderAvatarUrl) = currentTimelineItem.getSenderInfo()
val timeFormatter = DateFormat.getTimeInstance(DateFormat.SHORT)
val sentTime = timeFormatter.format(Date(currentTimelineItem.event.timestamp))
val senderAvatarData = AvatarData(
id = currentSender.value,
name = senderDisplayName ?: currentSender.value,
name = senderDisambiguatedDisplayName ?: currentSender.value,
url = senderAvatarUrl,
size = AvatarSize.TimelineSender
)
@ -72,7 +72,7 @@ class TimelineItemEventFactory @Inject constructor( @@ -72,7 +72,7 @@ class TimelineItemEventFactory @Inject constructor(
eventId = currentTimelineItem.eventId,
transactionId = currentTimelineItem.transactionId,
senderId = currentSender,
senderDisplayName = senderDisplayName,
senderDisambiguatedDisplayName = senderDisambiguatedDisplayName,
senderAvatar = senderAvatarData,
content = contentFactory.create(currentTimelineItem.event),
isMine = currentTimelineItem.event.isOwn,
@ -100,23 +100,23 @@ class TimelineItemEventFactory @Inject constructor( @@ -100,23 +100,23 @@ class TimelineItemEventFactory @Inject constructor(
}
private fun MatrixTimelineItem.Event.getSenderInfo(): Pair<String?, String?> {
val senderDisplayName: String?
val senderDisambiguatedDisplayName: String?
val senderAvatarUrl: String?
when (val senderProfile = event.senderProfile) {
ProfileTimelineDetails.Unavailable,
ProfileTimelineDetails.Pending,
is ProfileTimelineDetails.Error -> {
senderDisplayName = null
senderDisambiguatedDisplayName = null
senderAvatarUrl = null
}
is ProfileTimelineDetails.Ready -> {
senderDisplayName = senderProfile.getDisambiguatedDisplayName(event.sender)
senderDisambiguatedDisplayName = senderProfile.getDisambiguatedDisplayName(event.sender)
senderAvatarUrl = senderProfile.avatarUrl
}
}
return senderDisplayName to senderAvatarUrl
return senderDisambiguatedDisplayName to senderAvatarUrl
}
private fun MatrixTimelineItem.Event.computeReactionsState(): TimelineItemReactions {

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

@ -22,6 +22,7 @@ import io.element.android.libraries.matrix.api.permalink.PermalinkParser @@ -22,6 +22,7 @@ import io.element.android.libraries.matrix.api.permalink.PermalinkParser
import io.element.android.libraries.matrix.api.timeline.item.event.EventContent
import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo
import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails
import io.element.android.libraries.matrix.api.timeline.item.event.StickerContent
import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType
import io.element.android.libraries.matrix.ui.messages.toPlainText
@ -29,8 +30,7 @@ import io.element.android.libraries.matrix.ui.messages.toPlainText @@ -29,8 +30,7 @@ import io.element.android.libraries.matrix.ui.messages.toPlainText
data class InReplyToDetails(
val eventId: EventId,
val senderId: UserId,
val senderDisplayName: String?,
val senderAvatarUrl: String?,
val senderProfile: ProfileTimelineDetails,
val eventContent: EventContent?,
val textContent: String?,
)
@ -41,8 +41,7 @@ fun InReplyTo.map( @@ -41,8 +41,7 @@ fun InReplyTo.map(
is InReplyTo.Ready -> InReplyToDetails(
eventId = eventId,
senderId = senderId,
senderDisplayName = senderDisplayName,
senderAvatarUrl = senderAvatarUrl,
senderProfile = senderProfile,
eventContent = content,
textContent = when (content) {
is MessageContent -> {

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

@ -57,7 +57,7 @@ sealed interface TimelineItem { @@ -57,7 +57,7 @@ sealed interface TimelineItem {
val eventId: EventId? = null,
val transactionId: TransactionId? = null,
val senderId: UserId,
val senderDisplayName: String?,
val senderDisambiguatedDisplayName: String?,
val senderAvatar: AvatarData,
val content: TimelineItemEventContent,
val sentTime: String = "",
@ -74,7 +74,7 @@ sealed interface TimelineItem { @@ -74,7 +74,7 @@ sealed interface TimelineItem {
) : TimelineItem {
val showSenderInformation = groupPosition.isNew() && !isMine
val safeSenderName: String = senderDisplayName ?: senderId.value
val safeSenderName: String = senderDisambiguatedDisplayName ?: senderId.value
val failedToSend: Boolean = localSendState is LocalEventSendState.SendingFailed

2
features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/MessageEventFixtures.kt

@ -48,7 +48,7 @@ internal fun aMessageEvent( @@ -48,7 +48,7 @@ internal fun aMessageEvent(
id = eventId?.value.orEmpty(),
eventId = eventId,
senderId = A_USER_ID,
senderDisplayName = A_USER_NAME,
senderDisambiguatedDisplayName = A_USER_NAME,
senderAvatar = AvatarData(A_USER_ID.value, A_USER_NAME, size = AvatarSize.TimelineSender),
content = content,
sentTime = "",

44
features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactoryTest.kt

@ -82,7 +82,7 @@ class TimelineItemContentMessageFactoryTest { @@ -82,7 +82,7 @@ class TimelineItemContentMessageFactoryTest {
val sut = createTimelineItemContentMessageFactory()
val result = sut.create(
content = createMessageContent(type = OtherMessageType(msgType = "a_type", body = "body")),
senderDisplayName = "Bob",
senderDisambiguatedDisplayName = "Bob",
eventId = AN_EVENT_ID,
)
val expected = TimelineItemTextContent(
@ -100,7 +100,7 @@ class TimelineItemContentMessageFactoryTest { @@ -100,7 +100,7 @@ class TimelineItemContentMessageFactoryTest {
val sut = createTimelineItemContentMessageFactory()
val result = sut.create(
content = createMessageContent(type = LocationMessageType("body", "geo:1,2", "description")),
senderDisplayName = "Bob",
senderDisambiguatedDisplayName = "Bob",
eventId = AN_EVENT_ID,
)
val expected = TimelineItemLocationContent(
@ -116,7 +116,7 @@ class TimelineItemContentMessageFactoryTest { @@ -116,7 +116,7 @@ class TimelineItemContentMessageFactoryTest {
val sut = createTimelineItemContentMessageFactory()
val result = sut.create(
content = createMessageContent(type = LocationMessageType("body", "", null)),
senderDisplayName = "Bob",
senderDisambiguatedDisplayName = "Bob",
eventId = AN_EVENT_ID,
)
val expected = TimelineItemTextContent(
@ -134,7 +134,7 @@ class TimelineItemContentMessageFactoryTest { @@ -134,7 +134,7 @@ class TimelineItemContentMessageFactoryTest {
val sut = createTimelineItemContentMessageFactory()
val result = sut.create(
content = createMessageContent(type = TextMessageType("body", null)),
senderDisplayName = "Bob",
senderDisambiguatedDisplayName = "Bob",
eventId = AN_EVENT_ID,
)
val expected = TimelineItemTextContent(
@ -152,7 +152,7 @@ class TimelineItemContentMessageFactoryTest { @@ -152,7 +152,7 @@ class TimelineItemContentMessageFactoryTest {
val sut = createTimelineItemContentMessageFactory()
val result = sut.create(
content = createMessageContent(type = TextMessageType("https://www.example.org", null)),
senderDisplayName = "Bob",
senderDisambiguatedDisplayName = "Bob",
eventId = AN_EVENT_ID,
) as TimelineItemTextContent
val expected = TimelineItemTextContent(
@ -200,7 +200,7 @@ class TimelineItemContentMessageFactoryTest { @@ -200,7 +200,7 @@ class TimelineItemContentMessageFactoryTest {
formatted = FormattedBody(MessageFormat.HTML, expected.toString())
)
),
senderDisplayName = "Bob",
senderDisambiguatedDisplayName = "Bob",
eventId = AN_EVENT_ID,
)
assertThat((result as TimelineItemTextContent).formattedBody).isEqualTo(expected)
@ -218,7 +218,7 @@ class TimelineItemContentMessageFactoryTest { @@ -218,7 +218,7 @@ class TimelineItemContentMessageFactoryTest {
formatted = FormattedBody(MessageFormat.UNKNOWN, "formatted")
)
),
senderDisplayName = "Bob",
senderDisambiguatedDisplayName = "Bob",
eventId = AN_EVENT_ID,
)
assertThat((result as TimelineItemTextContent).formattedBody).isNull()
@ -229,7 +229,7 @@ class TimelineItemContentMessageFactoryTest { @@ -229,7 +229,7 @@ class TimelineItemContentMessageFactoryTest {
val sut = createTimelineItemContentMessageFactory()
val result = sut.create(
content = createMessageContent(type = VideoMessageType("body", null, null, MediaSource("url"), null)),
senderDisplayName = "Bob",
senderDisambiguatedDisplayName = "Bob",
eventId = AN_EVENT_ID,
)
val expected = TimelineItemVideoContent(
@ -277,7 +277,7 @@ class TimelineItemContentMessageFactoryTest { @@ -277,7 +277,7 @@ class TimelineItemContentMessageFactoryTest {
),
)
),
senderDisplayName = "Bob",
senderDisambiguatedDisplayName = "Bob",
eventId = AN_EVENT_ID,
)
val expected = TimelineItemVideoContent(
@ -303,7 +303,7 @@ class TimelineItemContentMessageFactoryTest { @@ -303,7 +303,7 @@ class TimelineItemContentMessageFactoryTest {
val sut = createTimelineItemContentMessageFactory()
val result = sut.create(
content = createMessageContent(type = AudioMessageType("body", MediaSource("url"), null)),
senderDisplayName = "Bob",
senderDisambiguatedDisplayName = "Bob",
eventId = AN_EVENT_ID,
)
val expected = TimelineItemAudioContent(
@ -332,7 +332,7 @@ class TimelineItemContentMessageFactoryTest { @@ -332,7 +332,7 @@ class TimelineItemContentMessageFactoryTest {
)
)
),
senderDisplayName = "Bob",
senderDisambiguatedDisplayName = "Bob",
eventId = AN_EVENT_ID,
)
val expected = TimelineItemAudioContent(
@ -351,7 +351,7 @@ class TimelineItemContentMessageFactoryTest { @@ -351,7 +351,7 @@ class TimelineItemContentMessageFactoryTest {
val sut = createTimelineItemContentMessageFactory()
val result = sut.create(
content = createMessageContent(type = VoiceMessageType("body", MediaSource("url"), null, null)),
senderDisplayName = "Bob",
senderDisambiguatedDisplayName = "Bob",
eventId = AN_EVENT_ID,
)
val expected = TimelineItemVoiceContent(
@ -384,7 +384,7 @@ class TimelineItemContentMessageFactoryTest { @@ -384,7 +384,7 @@ class TimelineItemContentMessageFactoryTest {
),
)
),
senderDisplayName = "Bob",
senderDisambiguatedDisplayName = "Bob",
eventId = AN_EVENT_ID,
)
val expected = TimelineItemVoiceContent(
@ -409,7 +409,7 @@ class TimelineItemContentMessageFactoryTest { @@ -409,7 +409,7 @@ class TimelineItemContentMessageFactoryTest {
)
val result = sut.create(
content = createMessageContent(type = VoiceMessageType("body", MediaSource("url"), null, null)),
senderDisplayName = "Bob",
senderDisambiguatedDisplayName = "Bob",
eventId = AN_EVENT_ID,
)
val expected = TimelineItemAudioContent(
@ -428,7 +428,7 @@ class TimelineItemContentMessageFactoryTest { @@ -428,7 +428,7 @@ class TimelineItemContentMessageFactoryTest {
val sut = createTimelineItemContentMessageFactory()
val result = sut.create(
content = createMessageContent(type = ImageMessageType("body", null, null, MediaSource("url"), null)),
senderDisplayName = "Bob",
senderDisambiguatedDisplayName = "Bob",
eventId = AN_EVENT_ID,
)
val expected = TimelineItemImageContent(
@ -499,7 +499,7 @@ class TimelineItemContentMessageFactoryTest { @@ -499,7 +499,7 @@ class TimelineItemContentMessageFactoryTest {
)
)
),
senderDisplayName = "Bob",
senderDisambiguatedDisplayName = "Bob",
eventId = AN_EVENT_ID,
)
val expected = TimelineItemImageContent(
@ -524,7 +524,7 @@ class TimelineItemContentMessageFactoryTest { @@ -524,7 +524,7 @@ class TimelineItemContentMessageFactoryTest {
val sut = createTimelineItemContentMessageFactory()
val result = sut.create(
content = createMessageContent(type = FileMessageType("body", MediaSource("url"), null)),
senderDisplayName = "Bob",
senderDisambiguatedDisplayName = "Bob",
eventId = AN_EVENT_ID,
)
val expected = TimelineItemFileContent(
@ -559,7 +559,7 @@ class TimelineItemContentMessageFactoryTest { @@ -559,7 +559,7 @@ class TimelineItemContentMessageFactoryTest {
)
)
),
senderDisplayName = "Bob",
senderDisambiguatedDisplayName = "Bob",
eventId = AN_EVENT_ID,
)
val expected = TimelineItemFileContent(
@ -578,7 +578,7 @@ class TimelineItemContentMessageFactoryTest { @@ -578,7 +578,7 @@ class TimelineItemContentMessageFactoryTest {
val sut = createTimelineItemContentMessageFactory()
val result = sut.create(
content = createMessageContent(type = NoticeMessageType("body", null)),
senderDisplayName = "Bob",
senderDisambiguatedDisplayName = "Bob",
eventId = AN_EVENT_ID,
)
val expected = TimelineItemNoticeContent(
@ -601,7 +601,7 @@ class TimelineItemContentMessageFactoryTest { @@ -601,7 +601,7 @@ class TimelineItemContentMessageFactoryTest {
formatted = FormattedBody(MessageFormat.HTML, "formatted")
)
),
senderDisplayName = "Bob",
senderDisambiguatedDisplayName = "Bob",
eventId = AN_EVENT_ID,
)
assertThat((result as TimelineItemNoticeContent).formattedBody).isEqualTo("formatted")
@ -612,7 +612,7 @@ class TimelineItemContentMessageFactoryTest { @@ -612,7 +612,7 @@ class TimelineItemContentMessageFactoryTest {
val sut = createTimelineItemContentMessageFactory()
val result = sut.create(
content = createMessageContent(type = EmoteMessageType("body", null)),
senderDisplayName = "Bob",
senderDisambiguatedDisplayName = "Bob",
eventId = AN_EVENT_ID,
)
val expected = TimelineItemEmoteContent(
@ -635,7 +635,7 @@ class TimelineItemContentMessageFactoryTest { @@ -635,7 +635,7 @@ class TimelineItemContentMessageFactoryTest {
formatted = FormattedBody(MessageFormat.HTML, "formatted")
)
),
senderDisplayName = "Bob",
senderDisambiguatedDisplayName = "Bob",
eventId = AN_EVENT_ID,
)
assertThat((result as TimelineItemEmoteContent).formattedBody).isEqualTo(SpannableString("* Bob formatted"))

2
features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouperTest.kt

@ -39,7 +39,7 @@ class TimelineItemGrouperTest { @@ -39,7 +39,7 @@ class TimelineItemGrouperTest {
id = "0",
senderId = A_USER_ID,
senderAvatar = anAvatarData(),
senderDisplayName = "",
senderDisambiguatedDisplayName = "",
content = TimelineItemStateEventContent(body = "a state event"),
reactionsState = aTimelineItemReactions(count = 0),
readReceiptState = TimelineItemReadReceipts(emptyList<ReadReceiptData>().toImmutableList()),

10
features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/model/InReplyToDetailTest.kt

@ -27,6 +27,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageTy @@ -27,6 +27,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageTy
import io.element.android.libraries.matrix.test.AN_EVENT_ID
import io.element.android.libraries.matrix.test.A_USER_ID
import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser
import io.element.android.libraries.matrix.test.timeline.aProfileTimelineDetails
import org.junit.Test
class InReplyToDetailTest {
@ -54,8 +55,7 @@ class InReplyToDetailTest { @@ -54,8 +55,7 @@ class InReplyToDetailTest {
val inReplyTo = InReplyTo.Ready(
eventId = AN_EVENT_ID,
senderId = A_USER_ID,
senderDisplayName = "senderDisplayName",
senderAvatarUrl = "senderAvatarUrl",
senderProfile = aProfileTimelineDetails(),
content = RoomMembershipContent(
userId = A_USER_ID,
change = MembershipChange.INVITED,
@ -73,8 +73,7 @@ class InReplyToDetailTest { @@ -73,8 +73,7 @@ class InReplyToDetailTest {
val inReplyTo = InReplyTo.Ready(
eventId = AN_EVENT_ID,
senderId = A_USER_ID,
senderDisplayName = "senderDisplayName",
senderAvatarUrl = "senderAvatarUrl",
senderProfile = aProfileTimelineDetails(),
content = MessageContent(
body = "**Hello!**",
inReplyTo = null,
@ -101,8 +100,7 @@ class InReplyToDetailTest { @@ -101,8 +100,7 @@ class InReplyToDetailTest {
val inReplyTo = InReplyTo.Ready(
eventId = AN_EVENT_ID,
senderId = A_USER_ID,
senderDisplayName = "senderDisplayName",
senderAvatarUrl = "senderAvatarUrl",
senderProfile = aProfileTimelineDetails(),
content = MessageContent(
body = "**Hello!**",
inReplyTo = null,

8
features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/model/InReplyToMetadataKtTest.kt

@ -42,6 +42,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.ImageMessageT @@ -42,6 +42,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.ImageMessageT
import io.element.android.libraries.matrix.api.timeline.item.event.LocationMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.OtherState
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails
import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent
import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent
import io.element.android.libraries.matrix.api.timeline.item.event.StateContent
@ -55,6 +56,7 @@ import io.element.android.libraries.matrix.test.A_USER_ID @@ -55,6 +56,7 @@ import io.element.android.libraries.matrix.test.A_USER_ID
import io.element.android.libraries.matrix.test.media.aMediaSource
import io.element.android.libraries.matrix.test.timeline.aMessageContent
import io.element.android.libraries.matrix.test.timeline.aPollContent
import io.element.android.libraries.matrix.test.timeline.aProfileTimelineDetails
import io.element.android.libraries.matrix.ui.components.A_BLUR_HASH
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailInfo
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailType
@ -430,15 +432,13 @@ class InReplyToMetadataKtTest { @@ -430,15 +432,13 @@ class InReplyToMetadataKtTest {
fun anInReplyToDetails(
eventId: EventId = AN_EVENT_ID,
senderId: UserId = A_USER_ID,
senderDisplayName: String? = "senderDisplayName",
senderAvatarUrl: String? = "senderAvatarUrl",
senderProfile: ProfileTimelineDetails = aProfileTimelineDetails(),
eventContent: EventContent? = aMessageContent(),
textContent: String? = "textContent",
) = InReplyToDetails(
eventId = eventId,
senderId = senderId,
senderDisplayName = senderDisplayName,
senderAvatarUrl = senderAvatarUrl,
senderProfile = senderProfile,
eventContent = eventContent,
textContent = textContent,
)

44
libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt

@ -41,7 +41,6 @@ import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessage @@ -41,7 +41,6 @@ import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessage
import io.element.android.libraries.matrix.api.timeline.item.event.OtherMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.PollContent
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails
import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent
import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent
import io.element.android.libraries.matrix.api.timeline.item.event.StateContent
@ -52,6 +51,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecry @@ -52,6 +51,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecry
import io.element.android.libraries.matrix.api.timeline.item.event.UnknownContent
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.getDisambiguatedDisplayName
import io.element.android.libraries.matrix.ui.messages.toPlainText
import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.services.toolbox.api.strings.StringProvider
@ -72,15 +72,13 @@ class DefaultRoomLastMessageFormatter @Inject constructor( @@ -72,15 +72,13 @@ class DefaultRoomLastMessageFormatter @Inject constructor(
override fun format(event: EventTimelineItem, isDmRoom: Boolean): CharSequence? {
val isOutgoing = event.isOwn
// Note: we do not use disambiguated display name here, see
// https://github.com/element-hq/element-x-ios/issues/1845#issuecomment-1888707428
val senderDisplayName = (event.senderProfile as? ProfileTimelineDetails.Ready)?.displayName ?: event.sender.value
val senderDisambiguatedDisplayName = event.senderProfile.getDisambiguatedDisplayName(event.sender)
return when (val content = event.content) {
is MessageContent -> processMessageContents(content, senderDisplayName, isDmRoom)
is MessageContent -> processMessageContents(content, senderDisambiguatedDisplayName, isDmRoom)
RedactedContent -> {
val message = sp.getString(CommonStrings.common_message_removed)
if (!isDmRoom) {
prefix(message, senderDisplayName)
prefix(message, senderDisambiguatedDisplayName)
} else {
message
}
@ -91,36 +89,40 @@ class DefaultRoomLastMessageFormatter @Inject constructor( @@ -91,36 +89,40 @@ class DefaultRoomLastMessageFormatter @Inject constructor(
is UnableToDecryptContent -> {
val message = sp.getString(CommonStrings.common_waiting_for_decryption_key)
if (!isDmRoom) {
prefix(message, senderDisplayName)
prefix(message, senderDisambiguatedDisplayName)
} else {
message
}
}
is RoomMembershipContent -> {
roomMembershipContentFormatter.format(content, senderDisplayName, isOutgoing)
roomMembershipContentFormatter.format(content, senderDisambiguatedDisplayName, isOutgoing)
}
is ProfileChangeContent -> {
profileChangeContentFormatter.format(content, event.sender, senderDisplayName, isOutgoing)
profileChangeContentFormatter.format(content, event.sender, senderDisambiguatedDisplayName, isOutgoing)
}
is StateContent -> {
stateContentFormatter.format(content, senderDisplayName, isOutgoing, RenderingMode.RoomList)
stateContentFormatter.format(content, senderDisambiguatedDisplayName, isOutgoing, RenderingMode.RoomList)
}
is PollContent -> {
val message = sp.getString(CommonStrings.common_poll_summary, content.question)
prefixIfNeeded(message, senderDisplayName, isDmRoom)
prefixIfNeeded(message, senderDisambiguatedDisplayName, isDmRoom)
}
is FailedToParseMessageLikeContent, is FailedToParseStateContent, is UnknownContent -> {
prefixIfNeeded(sp.getString(CommonStrings.common_unsupported_event), senderDisplayName, isDmRoom)
prefixIfNeeded(sp.getString(CommonStrings.common_unsupported_event), senderDisambiguatedDisplayName, isDmRoom)
}
is LegacyCallInviteContent -> sp.getString(CommonStrings.common_call_invite)
}?.take(MAX_SAFE_LENGTH)
}
private fun processMessageContents(messageContent: MessageContent, senderDisplayName: String, isDmRoom: Boolean): CharSequence? {
private fun processMessageContents(
messageContent: MessageContent,
senderDisambiguatedDisplayName: String,
isDmRoom: Boolean,
): CharSequence {
val internalMessage = when (val messageType: MessageType = messageContent.type) {
// Doesn't need a prefix
is EmoteMessageType -> {
return "* $senderDisplayName ${messageType.body}"
return "* $senderDisambiguatedDisplayName ${messageType.body}"
}
is TextMessageType -> {
messageType.toPlainText(permalinkParser)
@ -153,19 +155,23 @@ class DefaultRoomLastMessageFormatter @Inject constructor( @@ -153,19 +155,23 @@ class DefaultRoomLastMessageFormatter @Inject constructor(
messageType.body
}
}
return prefixIfNeeded(internalMessage, senderDisplayName, isDmRoom)
return prefixIfNeeded(internalMessage, senderDisambiguatedDisplayName, isDmRoom)
}
private fun prefixIfNeeded(message: String, senderDisplayName: String, isDmRoom: Boolean): CharSequence = if (isDmRoom) {
private fun prefixIfNeeded(
message: String,
senderDisambiguatedDisplayName: String,
isDmRoom: Boolean,
): CharSequence = if (isDmRoom) {
message
} else {
prefix(message, senderDisplayName)
prefix(message, senderDisambiguatedDisplayName)
}
private fun prefix(message: String, senderDisplayName: String): AnnotatedString {
private fun prefix(message: String, senderDisambiguatedDisplayName: String): AnnotatedString {
return buildAnnotatedString {
withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
append(senderDisplayName)
append(senderDisambiguatedDisplayName)
}
append(": ")
append(message)

8
libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultTimelineEventFormatter.kt

@ -49,16 +49,16 @@ class DefaultTimelineEventFormatter @Inject constructor( @@ -49,16 +49,16 @@ class DefaultTimelineEventFormatter @Inject constructor(
) : TimelineEventFormatter {
override fun format(event: EventTimelineItem): CharSequence? {
val isOutgoing = event.isOwn
val senderDisplayName = event.senderProfile.getDisambiguatedDisplayName(event.sender)
val senderDisambiguatedDisplayName = event.senderProfile.getDisambiguatedDisplayName(event.sender)
return when (val content = event.content) {
is RoomMembershipContent -> {
roomMembershipContentFormatter.format(content, senderDisplayName, isOutgoing)
roomMembershipContentFormatter.format(content, senderDisambiguatedDisplayName, isOutgoing)
}
is ProfileChangeContent -> {
profileChangeContentFormatter.format(content, event.sender, senderDisplayName, isOutgoing)
profileChangeContentFormatter.format(content, event.sender, senderDisambiguatedDisplayName, isOutgoing)
}
is StateContent -> {
stateContentFormatter.format(content, senderDisplayName, isOutgoing, RenderingMode.Timeline)
stateContentFormatter.format(content, senderDisambiguatedDisplayName, isOutgoing, RenderingMode.Timeline)
}
is LegacyCallInviteContent -> {
sp.getString(CommonStrings.common_call_invite)

11
libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/ProfileChangeContentFormatter.kt

@ -27,14 +27,19 @@ class ProfileChangeContentFormatter @Inject constructor( @@ -27,14 +27,19 @@ class ProfileChangeContentFormatter @Inject constructor(
fun format(
profileChangeContent: ProfileChangeContent,
senderId: UserId,
senderDisplayName: String,
senderDisambiguatedDisplayName: String,
senderIsYou: Boolean,
): String? = profileChangeContent.run {
val displayNameChanged = displayName != prevDisplayName
val avatarChanged = avatarUrl != prevAvatarUrl
return when {
avatarChanged && displayNameChanged -> {
val message = format(profileChangeContent.copy(avatarUrl = null, prevAvatarUrl = null), senderId, senderDisplayName, senderIsYou)
val message = format(
profileChangeContent = profileChangeContent.copy(avatarUrl = null, prevAvatarUrl = null),
senderId = senderId,
senderDisambiguatedDisplayName = senderDisambiguatedDisplayName,
senderIsYou = senderIsYou,
)
val avatarChangedToo = sp.getString(R.string.state_event_avatar_changed_too)
"$message\n$avatarChangedToo"
}
@ -63,7 +68,7 @@ class ProfileChangeContentFormatter @Inject constructor( @@ -63,7 +68,7 @@ class ProfileChangeContentFormatter @Inject constructor(
if (senderIsYou) {
sp.getString(R.string.state_event_avatar_url_changed_by_you)
} else {
sp.getString(R.string.state_event_avatar_url_changed, senderDisplayName)
sp.getString(R.string.state_event_avatar_url_changed, senderDisambiguatedDisplayName)
}
}
else -> null

22
libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/RoomMembershipContentFormatter.kt

@ -29,7 +29,7 @@ class RoomMembershipContentFormatter @Inject constructor( @@ -29,7 +29,7 @@ class RoomMembershipContentFormatter @Inject constructor(
) {
fun format(
membershipContent: RoomMembershipContent,
senderDisplayName: String,
senderDisambiguatedDisplayName: String,
senderIsYou: Boolean,
): CharSequence? {
val userId = membershipContent.userId
@ -48,24 +48,24 @@ class RoomMembershipContentFormatter @Inject constructor( @@ -48,24 +48,24 @@ class RoomMembershipContentFormatter @Inject constructor(
MembershipChange.BANNED, MembershipChange.KICKED_AND_BANNED -> if (senderIsYou) {
sp.getString(R.string.state_event_room_ban_by_you, userId.value)
} else {
sp.getString(R.string.state_event_room_ban, senderDisplayName, userId.value)
sp.getString(R.string.state_event_room_ban, senderDisambiguatedDisplayName, userId.value)
}
MembershipChange.UNBANNED -> if (senderIsYou) {
sp.getString(R.string.state_event_room_unban_by_you, userId.value)
} else {
sp.getString(R.string.state_event_room_unban, senderDisplayName, userId.value)
sp.getString(R.string.state_event_room_unban, senderDisambiguatedDisplayName, userId.value)
}
MembershipChange.KICKED -> if (senderIsYou) {
sp.getString(R.string.state_event_room_remove_by_you, userId.value)
} else {
sp.getString(R.string.state_event_room_remove, senderDisplayName, userId.value)
sp.getString(R.string.state_event_room_remove, senderDisambiguatedDisplayName, userId.value)
}
MembershipChange.INVITED -> if (senderIsYou) {
sp.getString(R.string.state_event_room_invite_by_you, userId.value)
} else if (memberIsYou) {
sp.getString(R.string.state_event_room_invite_you, senderDisplayName)
sp.getString(R.string.state_event_room_invite_you, senderDisambiguatedDisplayName)
} else {
sp.getString(R.string.state_event_room_invite, senderDisplayName, userId.value)
sp.getString(R.string.state_event_room_invite, senderDisambiguatedDisplayName, userId.value)
}
MembershipChange.INVITATION_ACCEPTED -> if (memberIsYou) {
sp.getString(R.string.state_event_room_invite_accepted_by_you)
@ -80,7 +80,7 @@ class RoomMembershipContentFormatter @Inject constructor( @@ -80,7 +80,7 @@ class RoomMembershipContentFormatter @Inject constructor(
MembershipChange.INVITATION_REVOKED -> if (senderIsYou) {
sp.getString(R.string.state_event_room_third_party_revoked_invite_by_you, userId.value)
} else {
sp.getString(R.string.state_event_room_third_party_revoked_invite, senderDisplayName, userId.value)
sp.getString(R.string.state_event_room_third_party_revoked_invite, senderDisambiguatedDisplayName, userId.value)
}
MembershipChange.KNOCKED -> if (memberIsYou) {
sp.getString(R.string.state_event_room_knock_by_you)
@ -90,7 +90,7 @@ class RoomMembershipContentFormatter @Inject constructor( @@ -90,7 +90,7 @@ class RoomMembershipContentFormatter @Inject constructor(
MembershipChange.KNOCK_ACCEPTED -> if (senderIsYou) {
sp.getString(R.string.state_event_room_knock_accepted_by_you, userId.value)
} else {
sp.getString(R.string.state_event_room_knock_accepted, senderDisplayName, userId.value)
sp.getString(R.string.state_event_room_knock_accepted, senderDisambiguatedDisplayName, userId.value)
}
MembershipChange.KNOCK_RETRACTED -> if (memberIsYou) {
sp.getString(R.string.state_event_room_knock_retracted_by_you)
@ -100,14 +100,14 @@ class RoomMembershipContentFormatter @Inject constructor( @@ -100,14 +100,14 @@ class RoomMembershipContentFormatter @Inject constructor(
MembershipChange.KNOCK_DENIED -> if (senderIsYou) {
sp.getString(R.string.state_event_room_knock_denied_by_you, userId.value)
} else if (memberIsYou) {
sp.getString(R.string.state_event_room_knock_denied_you, senderDisplayName)
sp.getString(R.string.state_event_room_knock_denied_you, senderDisambiguatedDisplayName)
} else {
sp.getString(R.string.state_event_room_knock_denied, senderDisplayName, userId.value)
sp.getString(R.string.state_event_room_knock_denied, senderDisambiguatedDisplayName, userId.value)
}
MembershipChange.NONE -> if (senderIsYou) {
sp.getString(R.string.state_event_room_none_by_you)
} else {
sp.getString(R.string.state_event_room_none, senderDisplayName)
sp.getString(R.string.state_event_room_none, senderDisambiguatedDisplayName)
}
MembershipChange.ERROR -> {
Timber.v("Filtering timeline item for room membership: $membershipContent")

18
libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/StateContentFormatter.kt

@ -29,7 +29,7 @@ class StateContentFormatter @Inject constructor( @@ -29,7 +29,7 @@ class StateContentFormatter @Inject constructor(
) {
fun format(
stateContent: StateContent,
senderDisplayName: String,
senderDisambiguatedDisplayName: String,
senderIsYou: Boolean,
renderingMode: RenderingMode,
): CharSequence? {
@ -39,15 +39,15 @@ class StateContentFormatter @Inject constructor( @@ -39,15 +39,15 @@ class StateContentFormatter @Inject constructor(
when {
senderIsYou && hasAvatarUrl -> sp.getString(R.string.state_event_room_avatar_changed_by_you)
senderIsYou && !hasAvatarUrl -> sp.getString(R.string.state_event_room_avatar_removed_by_you)
!senderIsYou && hasAvatarUrl -> sp.getString(R.string.state_event_room_avatar_changed, senderDisplayName)
else -> sp.getString(R.string.state_event_room_avatar_removed, senderDisplayName)
!senderIsYou && hasAvatarUrl -> sp.getString(R.string.state_event_room_avatar_changed, senderDisambiguatedDisplayName)
else -> sp.getString(R.string.state_event_room_avatar_removed, senderDisambiguatedDisplayName)
}
}
is OtherState.RoomCreate -> {
if (senderIsYou) {
sp.getString(R.string.state_event_room_created_by_you)
} else {
sp.getString(R.string.state_event_room_created, senderDisplayName)
sp.getString(R.string.state_event_room_created, senderDisambiguatedDisplayName)
}
}
is OtherState.RoomEncryption -> sp.getString(CommonStrings.common_encryption_enabled)
@ -56,8 +56,8 @@ class StateContentFormatter @Inject constructor( @@ -56,8 +56,8 @@ class StateContentFormatter @Inject constructor(
when {
senderIsYou && hasRoomName -> sp.getString(R.string.state_event_room_name_changed_by_you, content.name)
senderIsYou && !hasRoomName -> sp.getString(R.string.state_event_room_name_removed_by_you)
!senderIsYou && hasRoomName -> sp.getString(R.string.state_event_room_name_changed, senderDisplayName, content.name)
else -> sp.getString(R.string.state_event_room_name_removed, senderDisplayName)
!senderIsYou && hasRoomName -> sp.getString(R.string.state_event_room_name_changed, senderDisambiguatedDisplayName, content.name)
else -> sp.getString(R.string.state_event_room_name_removed, senderDisambiguatedDisplayName)
}
}
is OtherState.RoomThirdPartyInvite -> {
@ -68,7 +68,7 @@ class StateContentFormatter @Inject constructor( @@ -68,7 +68,7 @@ class StateContentFormatter @Inject constructor(
if (senderIsYou) {
sp.getString(R.string.state_event_room_third_party_invite_by_you, content.displayName)
} else {
sp.getString(R.string.state_event_room_third_party_invite, senderDisplayName, content.displayName)
sp.getString(R.string.state_event_room_third_party_invite, senderDisambiguatedDisplayName, content.displayName)
}
}
is OtherState.RoomTopic -> {
@ -76,8 +76,8 @@ class StateContentFormatter @Inject constructor( @@ -76,8 +76,8 @@ class StateContentFormatter @Inject constructor(
when {
senderIsYou && hasRoomTopic -> sp.getString(R.string.state_event_room_topic_changed_by_you, content.topic)
senderIsYou && !hasRoomTopic -> sp.getString(R.string.state_event_room_topic_removed_by_you)
!senderIsYou && hasRoomTopic -> sp.getString(R.string.state_event_room_topic_changed, senderDisplayName, content.topic)
else -> sp.getString(R.string.state_event_room_topic_removed, senderDisplayName)
!senderIsYou && hasRoomTopic -> sp.getString(R.string.state_event_room_topic_changed, senderDisambiguatedDisplayName, content.topic)
else -> sp.getString(R.string.state_event_room_topic_removed, senderDisambiguatedDisplayName)
}
}
is OtherState.Custom -> when (renderingMode) {

5
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt

@ -27,7 +27,7 @@ data class NotificationData( @@ -27,7 +27,7 @@ data class NotificationData(
val roomId: RoomId,
// mxc url
val senderAvatarUrl: String?,
// private, must use `getSenderName`
// private, must use `getDisambiguatedDisplayName`
private val senderDisplayName: String?,
private val senderIsNameAmbiguous: Boolean,
val roomAvatarUrl: String?,
@ -39,7 +39,7 @@ data class NotificationData( @@ -39,7 +39,7 @@ data class NotificationData(
val content: NotificationContent,
val hasMention: Boolean,
) {
fun getSenderName(userId: UserId): String = when {
fun getDisambiguatedDisplayName(userId: UserId): String = when {
senderDisplayName.isNullOrBlank() -> userId.value
senderIsNameAmbiguous -> "$senderDisplayName ($userId)"
else -> senderDisplayName
@ -52,6 +52,7 @@ sealed interface NotificationContent { @@ -52,6 +52,7 @@ sealed interface NotificationContent {
data class CallInvite(
val senderId: UserId,
) : MessageLike
data object CallHangup : MessageLike
data object CallCandidates : MessageLike
data object KeyVerificationReady : MessageLike

3
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/InReplyTo.kt

@ -33,8 +33,7 @@ sealed interface InReplyTo { @@ -33,8 +33,7 @@ sealed interface InReplyTo {
val eventId: EventId,
val content: EventContent,
val senderId: UserId,
val senderDisplayName: String?,
val senderAvatarUrl: String?,
val senderProfile: ProfileTimelineDetails,
) : InReplyTo
/**

5
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt

@ -35,7 +35,6 @@ import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageT @@ -35,7 +35,6 @@ import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageT
import io.element.android.libraries.matrix.impl.media.map
import org.matrix.rustcomponents.sdk.Message
import org.matrix.rustcomponents.sdk.MessageType
import org.matrix.rustcomponents.sdk.ProfileDetails
import org.matrix.rustcomponents.sdk.RepliedToEventDetails
import org.matrix.rustcomponents.sdk.use
import org.matrix.rustcomponents.sdk.FormattedBody as RustFormattedBody
@ -51,13 +50,11 @@ class EventMessageMapper { @@ -51,13 +50,11 @@ class EventMessageMapper {
val inReplyToId = EventId(details.eventId)
when (val event = details.event) {
is RepliedToEventDetails.Ready -> {
val senderProfile = event.senderProfile as? ProfileDetails.Ready
InReplyTo.Ready(
eventId = inReplyToId,
content = timelineEventContentMapper.map(event.content),
senderId = UserId(event.sender),
senderDisplayName = senderProfile?.displayName,
senderAvatarUrl = senderProfile?.avatarUrl,
senderProfile = event.senderProfile.map(),
)
}
is RepliedToEventDetails.Error -> InReplyTo.Error

18
libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt

@ -93,8 +93,8 @@ class NotifiableEventResolver @Inject constructor( @@ -93,8 +93,8 @@ class NotifiableEventResolver @Inject constructor(
): NotifiableEvent? {
return when (val content = this.content) {
is NotificationContent.MessageLike.RoomMessage -> {
val senderName = getSenderName(content.senderId)
val messageBody = descriptionFromMessageContent(content, senderName)
val senderDisambiguatedDisplayName = getDisambiguatedDisplayName(content.senderId)
val messageBody = descriptionFromMessageContent(content, senderDisambiguatedDisplayName)
val notificationBody = if (hasMention) {
stringProvider.getString(R.string.notification_mentioned_you_body, messageBody)
} else {
@ -107,7 +107,7 @@ class NotifiableEventResolver @Inject constructor( @@ -107,7 +107,7 @@ class NotifiableEventResolver @Inject constructor(
eventId = eventId,
noisy = isNoisy,
timestamp = this.timestamp,
senderName = senderName,
senderDisambiguatedDisplayName = senderDisambiguatedDisplayName,
body = notificationBody,
imageUriString = fetchImageIfPresent(client)?.toString(),
roomName = roomDisplayName,
@ -154,7 +154,7 @@ class NotifiableEventResolver @Inject constructor( @@ -154,7 +154,7 @@ class NotifiableEventResolver @Inject constructor(
eventId = eventId,
noisy = isNoisy,
timestamp = this.timestamp,
senderName = null,
senderDisambiguatedDisplayName = getDisambiguatedDisplayName(content.senderId),
body = stringProvider.getString(CommonStrings.common_call_invite),
imageUriString = fetchImageIfPresent(client)?.toString(),
roomName = roomDisplayName,
@ -180,7 +180,7 @@ class NotifiableEventResolver @Inject constructor( @@ -180,7 +180,7 @@ class NotifiableEventResolver @Inject constructor(
eventId = eventId,
noisy = isNoisy,
timestamp = this.timestamp,
senderName = getSenderName(content.senderId),
senderDisambiguatedDisplayName = getDisambiguatedDisplayName(content.senderId),
body = stringProvider.getString(CommonStrings.common_poll_summary, content.question),
imageUriString = null,
roomName = roomDisplayName,
@ -244,12 +244,12 @@ class NotifiableEventResolver @Inject constructor( @@ -244,12 +244,12 @@ class NotifiableEventResolver @Inject constructor(
private fun descriptionFromMessageContent(
content: NotificationContent.MessageLike.RoomMessage,
senderDisplayName: String,
senderDisambiguatedDisplayName: String,
): String {
return when (val messageType = content.messageType) {
is AudioMessageType -> messageType.body
is VoiceMessageType -> stringProvider.getString(CommonStrings.common_voice_message)
is EmoteMessageType -> "* $senderDisplayName ${messageType.body}"
is EmoteMessageType -> "* $senderDisambiguatedDisplayName ${messageType.body}"
is FileMessageType -> messageType.body
is ImageMessageType -> messageType.body
is StickerMessageType -> messageType.body
@ -310,7 +310,7 @@ private fun buildNotifiableMessageEvent( @@ -310,7 +310,7 @@ private fun buildNotifiableMessageEvent(
canBeReplaced: Boolean = false,
noisy: Boolean,
timestamp: Long,
senderName: String?,
senderDisambiguatedDisplayName: String?,
body: String?,
// We cannot use Uri? type here, as that could trigger a
// NotSerializableException when persisting this to storage
@ -335,7 +335,7 @@ private fun buildNotifiableMessageEvent( @@ -335,7 +335,7 @@ private fun buildNotifiableMessageEvent(
canBeReplaced = canBeReplaced,
noisy = noisy,
timestamp = timestamp,
senderName = senderName,
senderDisambiguatedDisplayName = senderDisambiguatedDisplayName,
body = body,
imageUriString = imageUriString,
threadId = threadId,

12
libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt

@ -45,7 +45,7 @@ class RoomGroupMessageCreator @Inject constructor( @@ -45,7 +45,7 @@ class RoomGroupMessageCreator @Inject constructor(
imageLoader: ImageLoader,
): RoomNotification.Message {
val lastKnownRoomEvent = events.last()
val roomName = lastKnownRoomEvent.roomName ?: lastKnownRoomEvent.senderName ?: "Room name (${roomId.value.take(8)}…)"
val roomName = lastKnownRoomEvent.roomName ?: lastKnownRoomEvent.senderDisambiguatedDisplayName ?: "Room name (${roomId.value.take(8)}…)"
val roomIsGroup = !lastKnownRoomEvent.roomIsDirect
val style = NotificationCompat.MessagingStyle(
Person.Builder()
@ -60,9 +60,9 @@ class RoomGroupMessageCreator @Inject constructor( @@ -60,9 +60,9 @@ class RoomGroupMessageCreator @Inject constructor(
}
val tickerText = if (roomIsGroup) {
stringProvider.getString(R.string.notification_ticker_text_group, roomName, events.last().senderName, events.last().description)
stringProvider.getString(R.string.notification_ticker_text_group, roomName, events.last().senderDisambiguatedDisplayName, events.last().description)
} else {
stringProvider.getString(R.string.notification_ticker_text_dm, events.last().senderName, events.last().description)
stringProvider.getString(R.string.notification_ticker_text_dm, events.last().senderDisambiguatedDisplayName, events.last().description)
}
val largeBitmap = getRoomBitmap(events, imageLoader)
@ -108,7 +108,7 @@ class RoomGroupMessageCreator @Inject constructor( @@ -108,7 +108,7 @@ class RoomGroupMessageCreator @Inject constructor(
null
} else {
Person.Builder()
.setName(event.senderName?.annotateForDebug(70))
.setName(event.senderDisambiguatedDisplayName?.annotateForDebug(70))
.setIcon(bitmapLoader.getUserIcon(event.senderAvatarPath, imageLoader))
.setKey(event.senderId.value)
.build()
@ -152,7 +152,7 @@ class RoomGroupMessageCreator @Inject constructor( @@ -152,7 +152,7 @@ class RoomGroupMessageCreator @Inject constructor(
private fun createFirstMessageSummaryLine(event: NotifiableMessageEvent, roomName: String, roomIsDirect: Boolean): CharSequence {
return if (roomIsDirect) {
buildSpannedString {
event.senderName?.let {
event.senderDisambiguatedDisplayName?.let {
inSpans(StyleSpan(Typeface.BOLD)) {
append(it)
append(": ")
@ -165,7 +165,7 @@ class RoomGroupMessageCreator @Inject constructor( @@ -165,7 +165,7 @@ class RoomGroupMessageCreator @Inject constructor(
inSpans(StyleSpan(Typeface.BOLD)) {
append(roomName)
append(": ")
event.senderName?.let {
event.senderDisambiguatedDisplayName?.let {
append(it)
append(" ")
}

3
libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt

@ -36,7 +36,7 @@ data class NotifiableMessageEvent( @@ -36,7 +36,7 @@ data class NotifiableMessageEvent(
val senderId: UserId,
val noisy: Boolean,
val timestamp: Long,
val senderName: String?,
val senderDisambiguatedDisplayName: String?,
val body: String?,
// We cannot use Uri? type here, as that could trigger a
// NotSerializableException when persisting this to storage
@ -55,7 +55,6 @@ data class NotifiableMessageEvent( @@ -55,7 +55,6 @@ data class NotifiableMessageEvent(
) : NotifiableEvent {
val type: String = EventType.MESSAGE
override val description: String = body ?: ""
val title: String = senderName ?: ""
// Example of value:
// content://io.element.android.x.debug.notifications.fileprovider/downloads/temp/notif/matrix.org/XGItzSDOnSyXjYtOPfiKexDJ

4
libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolverTest.kt

@ -462,7 +462,7 @@ class NotifiableEventResolverTest { @@ -462,7 +462,7 @@ class NotifiableEventResolverTest {
senderId = A_USER_ID_2,
noisy = false,
timestamp = A_TIMESTAMP,
senderName = null,
senderDisambiguatedDisplayName = null,
body = "Call in progress (unsupported)",
imageUriString = null,
threadId = null,
@ -586,7 +586,7 @@ class NotifiableEventResolverTest { @@ -586,7 +586,7 @@ class NotifiableEventResolverTest {
senderId = A_USER_ID_2,
noisy = false,
timestamp = A_TIMESTAMP,
senderName = "Bob",
senderDisambiguatedDisplayName = "Bob",
body = body,
imageUriString = null,
threadId = null,

2
libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fixtures/NotifiableEventFixture.kt

@ -85,7 +85,7 @@ fun aNotifiableMessageEvent( @@ -85,7 +85,7 @@ fun aNotifiableMessageEvent(
editedEventId = null,
noisy = false,
timestamp = timestamp,
senderName = "sender-name",
senderDisambiguatedDisplayName = "sender-name",
senderId = UserId("@sending-id:domain.com"),
body = "message-body",
roomId = roomId,

Loading…
Cancel
Save