Skip to content

Do not render unknown state events in the timeline - #7464

Open
hayaksi1 wants to merge 1 commit into
element-hq:developfrom
hayaksi1:fix/4095-hide-custom-state-events
Open

Do not render unknown state events in the timeline#7464
hayaksi1 wants to merge 1 commit into
element-hq:developfrom
hayaksi1:fix/4095-hide-custom-state-events

Conversation

@hayaksi1

Copy link
Copy Markdown
Contributor

Content

An unknown or custom state event was rendered in the timeline as the untranslated developer string Custom event <type>, while the room list already filtered the same event out. Any room whose members use a custom state event therefore shows rows of internal event types to every user, in English, whatever their locale.

The formatter now declines custom state events in both rendering modes, and the timeline drops events the formatter cannot render instead of coercing the missing text to an empty string. Both halves are needed: on its own, returning null from the formatter turns the wrong string into an empty tappable pill, because the timeline had no notion of "this event has nothing to display".

The filter is applied to the raw items before the item factory runs, rather than by returning null later. That matters for two reasons: the hidden event never becomes a neighbour when bubble grouping is computed, so the messages either side of it are not split into separate blocks for no visible reason; and a day divider whose only remaining event was hidden is dropped with it. This mirrors the existing keepDisplayablePinnedEvents precedent.

The SDK-side timeline filter cannot express this. It excludes an enumerated list of state event types, and the set of custom types is unbounded, so client-side filtering is the only option.

The issue also asks for a developer option to view hidden events (#3760). No such option exists in the codebase yet, so this ships the plain hide; the opt-in would be purely additive later.

Motivation and context

Part of #4095.

Tests

features/messages/impl/.../timeline/factories/DisplayableTimelineEventsTest.kt: a custom state event is dropped, a known state event and a message are kept, a day divider is dropped when its only event was hidden and kept when the day still has an event or a read marker.

features/messages/impl/.../timeline/factories/TimelineItemsFactoryTest.kt: a custom state event is not emitted and does not split the group of the messages around it. This fails on develop with None, Middle, None instead of First, Last, so it pins both the filtering and the fact that it happens before the group position is computed.

libraries/eventformatter/impl/.../StateContentFormatterTest.kt: a custom state event is not rendered in either rendering mode.

Run with ./gradlew :features:messages:impl:testDebugUnitTest :libraries:eventformatter:impl:testDebugUnitTest.

Read receipts and permalinks that land on a hidden event are not covered: as with the state types the SDK already excludes, they have nowhere to resolve to.

Tested devices

  • Physical
  • Emulator
  • OS version(s):

Checklist

  • I am aware of the etiquette.
  • This PR was made with the help of AI:
    • Yes. In this case, please request a review by Copilot.
    • No.
  • Changes have been tested on an Android device or Android emulator with API 24
  • 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
  • Pull request title will be used in the release note, it clearly defines what will change for the user
  • Pull request includes screenshots or videos if containing UI changes
  • You've made a self review of your PR

An unknown or custom state event was rendered in the timeline as the
untranslated developer string "Custom event <type>", while the room list
already filtered the same event out. Any room whose members use a custom
state event therefore shows rows of internal event types to every user.

The formatter now declines custom state events in both rendering modes, and
the timeline drops events the formatter cannot render instead of coercing the
missing text to an empty string. Filtering the raw items before the item
factory runs matters: returning null later would leave an empty tappable pill
behind, and would also split the bubble block of the messages either side of
the hidden event. A day divider whose only remaining event was hidden is
dropped with it.

The SDK-side timeline filter cannot express this, because it excludes
enumerated state event types and the set of custom types is unbounded.
@hayaksi1
hayaksi1 requested a review from a team as a code owner August 17, 2026 14:29
@hayaksi1
hayaksi1 requested review from jmartinesp and removed request for a team August 17, 2026 14:29
@github-actions

Copy link
Copy Markdown
Contributor

Thank you for your contribution! Here are a few things to check in the PR to ensure it's reviewed as quickly as possible:

  • If your pull request adds a feature or modifies the UI, this should have an equivalent pull request in the Element X iOS repo unless it only affects an Android-only behaviour or is behind a disabled feature flag, since we need parity in both clients to consider a feature done. It will also need to be approved by our product and design teams before being merged, so it's usually a good idea to discuss the changes in a Github issue first and then start working on them once the approach has been validated.
  • Your branch should be based on origin/develop, at least when it was created.
  • The title of the PR will be used for release notes, so it needs to describe the change visible to the user.
  • The test pass locally running ./gradlew test.
  • The code quality check suite pass locally running ./gradlew runQualityChecks.
  • If you modified anything related to the UI, including previews, you'll have to run the Record screenshots GH action in your forked repo: that will generate compatible new screenshots. However, given Github Actions limitations, it will prevent the CI from running temporarily, until you upload a new commit after that one. To do so, just pull the latest changes and push an empty commit.

@github-actions github-actions Bot added the Z-Community-PR Issue is solved by a community member's PR label Aug 17, 2026
@frebib

frebib commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Can I ask that instead of unconditionally removing these from the timeline, there's still an option to show them? Maybe a toggle in developer options?

@jmartinesp

Copy link
Copy Markdown
Member

I think we need some product decision for this. @mxandreas this is waiting for when you come back.

@jmartinesp jmartinesp added the X-Needs-Product Issue needs input from Product team label Aug 19, 2026
@jmartinesp

Copy link
Copy Markdown
Member

That said, this seems something that should use RustTimelineEventFilterFactory to filter out state events based on some FilterTimelineEventCondition. While there's not any for all custom/unknown event types, probably adding that condition in the SDK wouldn't be that hard.

@mxandreas

Copy link
Copy Markdown
Member

The issue also asks for a developer option to view hidden events (#3760). No such option exists in the codebase yet, so this ships the plain hide; the opt-in would be purely additive later.

Happy to help but not sure if I fully understand the core problem correctly. So, there can always be a case where the client just does not know an event type (perhaps it is a new event type and the client is old or it is some kind of custom event as mentioned) - correct?

I think hiding such an event isn't good because that can make "messages" disappear and create confusion. But I also agree that a hardcoded English message is also not great. Thus, ideally:

  1. The message is translatable and something a regular user can read, e.g. "[Sender] sent a message that can't be displayed".
  2. There's an option to hide/ignore such events - in case for some reason a user is starting to receive them a lot. I'd say in most cases it should be rare or?

@mxandreas

Copy link
Copy Markdown
Member

Also, I assume this applies only to non-state events and that in case the event has the fallback body, we display that instead?

@bxdxnn

bxdxnn commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

I think this duplicates #6862

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

Labels

X-Needs-Product Issue needs input from Product team Z-Community-PR Issue is solved by a community member's PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants