Skip to content

Set hasCurrentUserParticipated when the current user sends a thread reply - #5516

Open
nathanael-h wants to merge 1 commit into
matrix-org:developfrom
nathanael-h:nh/thread-participated-on-local-reply
Open

Set hasCurrentUserParticipated when the current user sends a thread reply#5516
nathanael-h wants to merge 1 commit into
matrix-org:developfrom
nathanael-h:nh/thread-participated-on-local-reply

Conversation

@nathanael-h

@nathanael-h nathanael-h commented Sep 2, 2026

Copy link
Copy Markdown

Fixes #5515

Problem

Thread.hasCurrentUserParticipated is only ever populated from the homeserver's bundled current_user_participated field on the thread root's aggregated m.thread relation (processRootEvent()), and cleared on full thread redaction. Nothing sets it when the current user actually replies.

onEcho() does call updateThreadMetadata(), but the root-bundle refresh is guarded to run only once (if (!this.processRootEventPromise)), so no re-fetch happens. The flag therefore stays false immediately after the local user replies in a thread they hadn't participated in, until the server re-sends the root event's bundled relation — which, outside a gappy/limited sync, may not happen for a long time.

Consequences: Room.updateThreadRootEvents only adds a root to the participated timeline set when the flag is set, so the per-room Threads panel "My threads" filter drops just-replied threads on the live path (usually masked by the authoritative ?include=participated seed on open); and element-web's Threads Activity Centre mis-sorts such a thread into Other threads. See the issue for the full analysis.

Change

In addEvent(), latch the flag when the current user authors an m.thread reply:

if (event.getSender() === this.client.getUserId() && event.isRelation(THREAD_RELATION_TYPE.name)) {
    this._currentUserParticipated = true;
}
  • Gated on THREAD_RELATION_TYPE rather than any relation, so the current user's own reactions and edits don't count as participation — matching the server's current_user_participated semantics. (THREAD_RELATION_TYPE is already in scope; no new imports.)
  • Placed before the Annotation/Replace early-return branch so it isn't skipped, but the relation gate means those events still don't latch.
  • addEvent() is the common path for local echo and remote sync events, so this covers both "I just replied here" and "my reply arrived from another device".
  • The existing redaction path still clears the flag, and the server bundle remains the authoritative source.

Known limitation (deliberate)

A gappy sync (onTimelineReset) or a redaction of the thread's last event resets processRootEventPromise, so a later updateThreadMetadata() re-runs processRootEvent() and overwrites the flag from the server bundle. By that point the server has normally registered our reply, so it comes back as true; but a refresh landing between local echo and the send being acked could briefly regress it to false.

I kept the change minimal rather than making the latch sticky, since stickiness would need care around redaction of the user's only reply. Happy to make it sticky instead if you'd prefer that.

Tests

New describe("hasCurrentUserParticipated") block in spec/unit/models/thread.spec.ts covering: the current user's reply sets the flag (fails without this change), another user's reply does not, and the current user's reaction does not.

Tests  26 passed (26)   spec/unit/models/thread.spec.ts

spec/unit/models/room.spec.ts, spec/unit/room.spec.ts and spec/integ/matrix-client-syncing.spec.ts also pass (300 tests). tsc --noEmit reports no errors in the changed files.

Downstream

element-web currently works around this in useUnreadThreadRooms.ts (hasCurrentUserSentInThread, inspecting the local timeline for a sent m.thread reply) — see element-hq/element-web#32851. Once this lands and element-web bumps its js-sdk, that can be dropped.

Checklist

  • Tests written for new code (and old code if feasible).
  • New or updated public/exported symbols have accurate TSDoc documentation.
  • Linter and other CI checks pass. (Tests and tsc run locally; oxlint/oxfmt were not runnable in my checkout, so relying on CI for those — happy to push a fixup if the formatter disagrees.)
  • Sign-off given on the changes (see CONTRIBUTING.md).

I cannot apply labels as a community contributor, so the Preview Changelog check is failing on the missing type label — please add T-Defect (the PR title works as the changelog entry).

…eply

`Thread.hasCurrentUserParticipated` was only ever populated from the
server's bundled `current_user_participated` flag on the thread root, so
it stayed false right after the local user replied in a thread until the
next root event bundle refresh. Participated views ("My threads" in the
per-room Threads panel, the Threads Activity Centre) therefore dropped
just-sent replies.

Latch the flag when the current user authors an m.thread reply, matching
the server's participated semantics (a thread relation, not a reaction
or an edit).

Fixes matrix-org#5515

Signed-off-by: Nathanaël HANNEBERT <nathanael.hannebert@vates.tech>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T-Defect 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.

Thread.hasCurrentUserParticipated stays false after the current user replies

2 participants