Skip to content

feat[matrix-sdk-base]: add prev_content as a fallback in case of missing display name and avatar url (#6026)#6097

Open
JoFrost wants to merge 12 commits intomatrix-org:mainfrom
JoFrost:main
Open

feat[matrix-sdk-base]: add prev_content as a fallback in case of missing display name and avatar url (#6026)#6097
JoFrost wants to merge 12 commits intomatrix-org:mainfrom
JoFrost:main

Conversation

@JoFrost
Copy link
Contributor

@JoFrost JoFrost commented Feb 2, 2026

Hello! This PR implements a potential fix for the issue #6026, following the investigation of the issue.

To briefly restate the context, during member sync, the SDK stores the full m.room.member event, but display name resolution relies only on RoomMemberEventContent. In some cases (for example when a user leaves a room), the current content no longer contains the profile fields needed to compute a display name, even though they are still available in prev_content.

This PR updates RoomMember::display_name to fall back to prev_content when the current content lacks the required fields, allowing the last known display name to be used.

This may not be the most idiomatic solution, but it seems to behave reliably in practice. The intent of this PR is to open discussion around whether this behavior is desirable and/or where it should be handled.

  • Public API changes documented in changelogs (optional)

Signed-off-by:

JoFrost and others added 2 commits February 2, 2026 13:20
…ing display name and avatar url

Signed-off-by: JoFrost <20685007+JoFrost@users.noreply.github.com>
@JoFrost JoFrost requested a review from a team as a code owner February 2, 2026 11:51
@JoFrost JoFrost requested review from andybalaam and removed request for a team February 2, 2026 11:51
@codecov
Copy link

codecov bot commented Feb 2, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.80%. Comparing base (036c5d3) to head (0193b97).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #6097   +/-   ##
=======================================
  Coverage   89.79%   89.80%           
=======================================
  Files         363      363           
  Lines      100499   100499           
  Branches   100499   100499           
=======================================
+ Hits        90243    90249    +6     
+ Misses       6718     6711    -7     
- Partials     3538     3539    +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@codspeed-hq
Copy link

codspeed-hq bot commented Feb 2, 2026

Merging this PR will not alter performance

✅ 50 untouched benchmarks


Comparing JoFrost:main (0193b97) with main (036c5d3)

Open in CodSpeed

Copy link
Member

@bnjbvr bnjbvr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for writing the patch! Looks like it's on a great path. Would you be so kind as to include some new unit tests, that show the new behavior? They wouldn't pass in main, but would pass with the new changes you've incorporated in your PR.

} else {
self.event.displayname_value()
// Profile cache first
// Not to fond of this double as_ref() chain, but it avoids an extra clone.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the second .as_ref() should be necessary; the compiler ought to call .as_original(&self), then the name is a deref so already a reference. Not sure what this .as_ref() call does?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because self.profile is an Arc<Option<_>>, I needed to borrow through both the Arc and the Option to access the inner value without moving it. However, I realized I overcomplicated this, so I reverted to the way it was before.

p.as_original().and_then(|e| e.content.displayname.as_deref())
} else {
self.event.displayname_value()
// Profile cache first
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Profile cache first
// Try from the cached profile first.

return Some(name);
}

// Then current event content
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Then current event content
// Then from the current event's content.

return Some(name);
}

// Last resort: prev_content
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Last resort: prev_content
// As a last resort, try the previous content.

p.as_original().and_then(|e| e.content.avatar_url.as_deref())
} else {
self.event.avatar_url()
if let Some(url) = self
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be sweet to:

  1. have the same kind of comments around here
  2. remove the extraneous .as_ref() call below

@bnjbvr bnjbvr removed the request for review from andybalaam February 2, 2026 13:28
@JoFrost
Copy link
Contributor Author

JoFrost commented Feb 2, 2026

All done, thanks for your feedbacks! @bnjbvr

JoFrost and others added 2 commits February 3, 2026 11:49
Signed-off-by: JoFrost <20685007+JoFrost@users.noreply.github.com>
@bnjbvr bnjbvr self-requested a review February 3, 2026 12:09
@bnjbvr
Copy link
Member

bnjbvr commented Feb 4, 2026

I'm putting this on hold at the moment, because we've discovered this might be intentional, after all (sorry about that!). If a user has been kicked of a room (which materializes by a leave m.room.member event, that will have no avatar or displayname set), because for example, their name contained profanities, then we wouldn't want to keep displaying their name all the time.

An alternative would be to not update timeline items predating the membership change, but this wouldn't address the moderation use case above. I'll chat with our internal UX folks on their opinions about this and will get back to this PR then.

@JoFrost
Copy link
Contributor Author

JoFrost commented Feb 4, 2026

No worries! Thanks for letting me know.

Copy link
Member

@bnjbvr bnjbvr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I've had the possibility to discuss this internally, and we came to the following conclusion:

  • we would like to maintain the latest known profile of a user, when they left (either because kicked or upon intentional leave)
  • but we'd like a banned user to lose their profile (that's #6119)

As a result, we can't just use the prev_content in the room membership field, because that would make implementing the second bullet point impossible. Thus, I think we need a different approach here, but the good news is that it might be simple enough.

if event.state_key() == event.sender() {

Here, we could append the condition "and it is not a leave membership change". With that, I think we ought to keep the desired behavior.

For bonus points, you can also fix #6119 by updating the condition at line 44, and add a test for it. But don't feel any obligation to do so, of course :-)

@JoFrost
Copy link
Contributor Author

JoFrost commented Feb 5, 2026

Actually that's cool, I can take care of it! It looks indeed very easy to handle, and I'll write an unit test for it!

@JoFrost
Copy link
Contributor Author

JoFrost commented Feb 5, 2026

I think this is good now.

Banned users no longer resolve a profile in the timeline, while users who leave or get kicked still keep their last known profile. I was also able to remove the prev_content fallback entirely, and everything continues to behave correctly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants