Repository: element-hq/element-x-android (language: kotlin)

Issue:
Show new name just once, not twice in display name change events
### Your use case

#### What would you like to do?
See issue title.

#### Why would you like to do it?
It looks quite confusing at first glance, especially with long names that include sentences or `away` text:
![Screenshot_20230927-125549_Element X](https://github.com/element-hq/element-x-android/assets/46846000/256c2a29-06d0-40ae-b2e8-793db58f35ec)

#### How would you like to achieve it?
I think it would be sufficient to change the string like so: `$old_name changed their display name to $new_name`.


### Have you considered any alternatives?

I don't see anything better than simply showing `old -> new` instead of what's right now.

### Additional context

Maybe it'd be nicer for readability if the display names in that event were somehow highlighted, or just looked different than the `changed their display name` text, especially with long names like in screenshot above.

### Are you willing to provide a PR?

Yes


Discussion hints:
nan

Pull request: Use `userId` on display name change state events
<!-- Please read [CONTRIBUTING.md](https://github.com/element-hq/element-x-android/blob/develop/CONTRIBUTING.md) before submitting your pull request -->
 
## Type of change

- [ ] Feature
- [x] Bugfix
- [ ] Technical
- [ ] Other :

## Content

- Replaces the first usage of the user's display name with its user id in display name change state events.

## Motivation and context

Partially fixes #2125 . It may not be the ideal solution, but until we decide how to handle this in both platforms, we should just follow what iOS currently does.

## Tests

<!-- Explain how you tested your development -->

- Change the display name of a user.
- Open a room that user has joined.

You should see a `{userId} changed their display name from {old} to {new}` state event.

## Tested devices

- [x] Physical
- [ ] Emulator
- OS version(s): 14

## Checklist

<!-- Depending on the Pull Request content, it can be acceptable if some of the following checkboxes stay unchecked. -->

- [ ] Changes have been tested on an Android device or Android emulator with API 23
- [ ] UI change has been tested on both light and dark themes
- [ ] Accessibility has been taken into account. See https://github.com/element-hq/element-x-android/blob/develop/CONTRIBUTING.md#accessibility
- [ ] Pull request is based on the develop branch
- [x] Pull request includes a new file under ./changelog.d. See https://github.com/element-hq/element-x-android/blob/develop/CONTRIBUTING.md#changelog
- [ ] Pull request includes screenshots or videos if containing UI changes
- [ ] Pull request includes a [sign off](https://matrix-org.github.io/synapse/latest/development/contributing_guide.html#sign-off)
- [x] You've made a self review of your PR


Test command: ./gradlew :libraries:eventformatter:impl:testDebugUnitTest --tests "io.element.android.libraries.eventformatter.impl.DefaultRoomLastMessageFormatterTest"

Fix patch:
diff --git a/changelog.d/2125.bugfix b/changelog.d/2125.bugfix
new file mode 100644
index 00000000000..95e6fb769d0
--- /dev/null
+++ b/changelog.d/2125.bugfix
@@ -0,0 +1,1 @@
+Use the display name only once in display name change events. The user should be referenced by `userId` instead.
diff --git a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt
index e9066681c56..cfc54e5ed1b 100644
--- a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt
+++ b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt
@@ -97,7 +97,7 @@ class DefaultRoomLastMessageFormatter @Inject constructor(
                 roomMembershipContentFormatter.format(content, senderDisplayName, isOutgoing)
             }
             is ProfileChangeContent -> {
-                profileChangeContentFormatter.format(content, senderDisplayName, isOutgoing)
+                profileChangeContentFormatter.format(content, event.sender, senderDisplayName, isOutgoing)
             }
             is StateContent -> {
                 stateContentFormatter.format(content, senderDisplayName, isOutgoing, RenderingMode.RoomList)
diff --git a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultTimelineEventFormatter.kt b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultTimelineEventFormatter.kt
index e147f25e166..efd91270d39 100644
--- a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultTimelineEventFormatter.kt
+++ b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultTimelineEventFormatter.kt
@@ -54,7 +54,7 @@ class DefaultTimelineEventFormatter @Inject constructor(
                 roomMembershipContentFormatter.format(content, senderDisplayName, isOutgoing)
             }
             is ProfileChangeContent -> {
-                profileChangeContentFormatter.format(content, senderDisplayName, isOutgoing)
+                profileChangeContentFormatter.format(content, event.sender, senderDisplayName, isOutgoing)
             }
             is StateContent -> {
                 stateContentFormatter.format(content, senderDisplayName, isOutgoing, RenderingMode.Timeline)
diff --git a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/ProfileChangeContentFormatter.kt b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/ProfileChangeContentFormatter.kt
index aea43298f9e..1b27c4ec158 100644
--- a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/ProfileChangeContentFormatter.kt
+++ b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/ProfileChangeContentFormatter.kt
@@ -16,6 +16,7 @@
 
 package io.element.android.libraries.eventformatter.impl
 
+import io.element.android.libraries.matrix.api.core.UserId
 import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent
 import io.element.android.services.toolbox.api.strings.StringProvider
 import javax.inject.Inject
@@ -25,6 +26,7 @@ class ProfileChangeContentFormatter @Inject constructor(
 ) {
     fun format(
         profileChangeContent: ProfileChangeContent,
+        senderId: UserId,
         senderDisplayName: String,
         senderIsYou: Boolean,
     ): String? = profileChangeContent.run {
@@ -32,7 +34,7 @@ class ProfileChangeContentFormatter @Inject constructor(
         val avatarChanged = avatarUrl != prevAvatarUrl
         return when {
             avatarChanged && displayNameChanged -> {
-                val message = format(profileChangeContent.copy(avatarUrl = null, prevAvatarUrl = null), senderDisplayName, senderIsYou)
+                val message = format(profileChangeContent.copy(avatarUrl = null, prevAvatarUrl = null), senderId, senderDisplayName, senderIsYou)
                 val avatarChangedToo = sp.getString(R.string.state_event_avatar_changed_too)
                 "$message\n$avatarChangedToo"
             }
@@ -41,19 +43,19 @@ class ProfileChangeContentFormatter @Inject constructor(
                     if (senderIsYou) {
                         sp.getString(R.string.state_event_display_name_changed_from_by_you, prevDisplayName, displayName)
                     } else {
-                        sp.getString(R.string.state_event_display_name_changed_from, senderDisplayName, prevDisplayName, displayName)
+                        sp.getString(R.string.state_event_display_name_changed_from, senderId.value, prevDisplayName, displayName)
                     }
                 } else if (displayName != null) {
                     if (senderIsYou) {
                         sp.getString(R.string.state_event_display_name_set_by_you, displayName)
                     } else {
-                        sp.getString(R.string.state_event_display_name_set, senderDisplayName, displayName)
+                        sp.getString(R.string.state_event_display_name_set, senderId.value, displayName)
                     }
                 } else {
                     if (senderIsYou) {
                         sp.getString(R.string.state_event_display_name_removed_by_you, prevDisplayName)
                     } else {
-                        sp.getString(R.string.state_event_display_name_removed, senderDisplayName, prevDisplayName)
+                        sp.getString(R.string.state_event_display_name_removed, senderId.value, prevDisplayName)
                     }
                 }
             }


Test patch:
diff --git a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTest.kt b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTest.kt
index b127911cc06..71860937b48 100644
--- a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTest.kt
+++ b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTest.kt
@@ -733,7 +733,7 @@ class DefaultRoomLastMessageFormatterTest {
 
         val someoneChangedDisplayNameEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = changedContent)
         val someoneChangedDisplayName = formatter.format(someoneChangedDisplayNameEvent, false)
-        assertThat(someoneChangedDisplayName).isEqualTo("$otherName changed their display name from $oldDisplayName to $newDisplayName")
+        assertThat(someoneChangedDisplayName).isEqualTo("$someoneElseId changed their display name from $oldDisplayName to $newDisplayName")
 
         val youSetDisplayNameEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = setContent)
         val youSetDisplayName = formatter.format(youSetDisplayNameEvent, false)
@@ -741,7 +741,7 @@ class DefaultRoomLastMessageFormatterTest {
 
         val someoneSetDisplayNameEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = setContent)
         val someoneSetDisplayName = formatter.format(someoneSetDisplayNameEvent, false)
-        assertThat(someoneSetDisplayName).isEqualTo("$otherName set their display name to $newDisplayName")
+        assertThat(someoneSetDisplayName).isEqualTo("$someoneElseId set their display name to $newDisplayName")
 
         val youRemovedDisplayNameEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = removedContent)
         val youRemovedDisplayName = formatter.format(youRemovedDisplayNameEvent, false)
@@ -749,7 +749,7 @@ class DefaultRoomLastMessageFormatterTest {
 
         val someoneRemovedDisplayNameEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = removedContent)
         val someoneRemovedDisplayName = formatter.format(someoneRemovedDisplayNameEvent, false)
-        assertThat(someoneRemovedDisplayName).isEqualTo("$otherName removed their display name (it was $oldDisplayName)")
+        assertThat(someoneRemovedDisplayName).isEqualTo("$someoneElseId removed their display name (it was $oldDisplayName)")
 
         val unchangedEvent = createRoomEvent(sentByYou = true, senderDisplayName = otherName, content = sameContent)
         val unchangedResult = formatter.format(unchangedEvent, false)
@@ -828,7 +828,7 @@ class DefaultRoomLastMessageFormatterTest {
     // endregion
 
     private fun createRoomEvent(sentByYou: Boolean, senderDisplayName: String?, content: EventContent): EventTimelineItem {
-        val sender = if (sentByYou) A_USER_ID else UserId("@someone_else:domain")
+        val sender = if (sentByYou) A_USER_ID else someoneElseId
         val profile = ProfileTimelineDetails.Ready(senderDisplayName, false, null)
         return anEventTimelineItem(
             content = content,
@@ -837,4 +837,6 @@ class DefaultRoomLastMessageFormatterTest {
             isOwn = sentByYou,
         )
     }
+
+    private val someoneElseId = UserId("@someone_else:domain")
 }