Skip to content

[2.x] fix(api): surface event posts in discussion PATCH responses and route title via rename()#4658

Merged
imorland merged 2 commits into
2.xfrom
im/discussion-update-posts-linkage
May 13, 2026
Merged

[2.x] fix(api): surface event posts in discussion PATCH responses and route title via rename()#4658
imorland merged 2 commits into
2.xfrom
im/discussion-update-posts-linkage

Conversation

@imorland
Copy link
Copy Markdown
Member

Summary

Fixes two related 2.x regressions where discussion changes silently fail to surface in the UI:

  1. Event posts created by mergePost() (sticky, tags, lock) are absent from PATCH responses. The 1.x UpdateDiscussionController refreshed posts linkage and inlined modified posts on every update; the 2.x DiscussionResource rewrite dropped that. Result: the action originator sees no event post in their stream until reload — even though the post exists in the DB.
  2. Renaming a discussion no longer raises Renamed. The 2.x DiscussionResource defines a title field that delegates to the default attribute setter, bypassing Discussion::rename(). The DiscussionRenamedLogger listener never runs, so no discussionRenamed event post is created and no notification is sent to the discussion author.

Together these regressions break the "rename → event post appears → original author gets notified" flow that worked end-to-end in 1.x.

Changes

Core

  • framework/core/src/Api/Resource/DiscussionResource.php

    • Expand the posts field's withLinkage predicate to also fire on update. PATCH responses now carry the refreshed posts linkage including any new event post id(s) added via mergePost(). Frontend stream.update() then sees count() grow and loadRange() fetches the new post(s) in a single batched GET.
    • Add a ->set() callback on the title field so updates route through Discussion::rename(). This restores the Renamed domain event, which in turn triggers DiscussionRenamedLogger to create the discussionRenamed event post and dispatch DiscussionRenamedBlueprint notifications.
  • framework/core/js/src/forum/components/RenameDiscussionModal.tsx — Chain m.redraw() onto the stream.update() promise rather than calling it synchronously. Without this, the redraw fires before loadRange() resolves and the new event post stays invisible until something else triggers a redraw. Same pattern as flarum/realtime's existing websocketEventStreamUpdate handler.

Sticky, Tags, Lock (client timing)

  • addStickyControl.js, TagDiscussionModal.tsx, addLockControl.js — same update().then(m.redraw) chaining fix. Lock had server-side realtime wired correctly but its client-side redraw timing was still off; sticky and tags had neither.

Sticky, Tags (realtime broadcast)

  • extensions/sticky/extend.php — Register DiscussionWasStickied / DiscussionWasUnstickied via the Realtime extender's broadcastModelEvent, broadcasting as 'stickiedEvent'. Other users viewing the discussion see the event post in real time.
  • extensions/sticky/js/src/forum/extendRealtime.ts (new) — register 'stickiedEvent' via the Realtime JS extender's onDiscussionStreamEvent, mirroring flarum-lock.
  • extensions/tags/extend.php — Same wiring for DiscussionWasTagged, broadcasting as 'taggedEvent'. Recipient permission is enforced per-user by the Realtime extension's existing Generator (it issues an internal API request as the recipient, so users without access to a discussion don't receive the broadcast).
  • extensions/tags/js/src/forum/extendRealtime.ts (new) — register 'taggedEvent'.

Tests

  • framework/core/tests/integration/api/discussions/UpdateTest.php (new) — asserts that PATCHing title creates a discussionRenamed post, that the PATCH response's posts linkage contains its id, and that a no-op title change creates no event post.
  • extensions/sticky/tests/integration/api/StickyDiscussionsTest.php — new test asserting that PATCH isSticky: true returns a response whose posts linkage contains both the original comment post and the newly-created discussionStickied event post.

Manual verification

On a Flarum 2.0.0-rc.1 stack with flarum-realtime enabled:

  • Same-user (no realtime needed): sticky / unsticky / tag-edit / lock / rename → event post appears within one network round-trip, no reload.
  • Cross-user (realtime needed): a second logged-in browser viewing the same discussion sees the event post appear automatically when the first browser performs any of the above actions. Previously worked for lock only; now works for all four.
  • Rename notification: when User B renames User A's discussion, User A receives the discussionRenamed notification (provided the preference is enabled).
  • Permission gating: users without access to a discussion (via tag visibility) do not receive realtime broadcasts about it — verified by inspecting the Realtime extension's Generator, which runs the payload-generation API request as the recipient.

Closes #4620

… route title via rename()

Fixes two related 2.x regressions where discussion changes silently
fail to surface in the UI:

1. Event posts created via `mergePost()` (sticky/tags/lock) were absent
   from PATCH responses. The 1.x `UpdateDiscussionController` refreshed
   the `posts` linkage and inlined modified posts on every update; the
   2.x `DiscussionResource` rewrite dropped that.

2. Renaming a discussion no longer raised `Renamed`. The 2.x
   `DiscussionResource` title field delegated to the default attribute
   setter, bypassing `Discussion::rename()`. The
   `DiscussionRenamedLogger` listener never ran, so neither the
   `discussionRenamed` event post nor the notification fired.

Together these broke the "rename -> event post appears -> author gets
notified" flow that worked end-to-end in 1.x.

Core fixes:
- `DiscussionResource::posts` field now exposes linkage on update
- `DiscussionResource::title` field now routes through `rename()`
- `RenameDiscussionModal` chains redraw onto the `update()` promise

Sticky/Tags/Lock JS:
- Chain redraw onto the `stream.update()` promise (was firing
  synchronously, before the new post was in-store)

Sticky/Tags realtime:
- Register broadcasts via the Realtime extender so other users see
  the event posts as they happen, mirroring the existing `lock`
  template. Recipient permission is enforced per-user by the Realtime
  Generator (internal API request runs as the recipient).

Closes #4620
@imorland imorland requested a review from a team as a code owner May 13, 2026 09:43
@imorland imorland modified the milestone: 2.0.0-rc.2 May 13, 2026
…config

build-typings was failing because the new `extendRealtime.ts` files
import from `ext:flarum/realtime/forum/extenders/Realtime`, but
neither extension's tsconfig.json mapped that module specifier to
realtime's dist-typings. Mirror the mapping that flarum-lock already
has.
@imorland imorland merged commit 4c2e44f into 2.x May 13, 2026
25 checks passed
@imorland imorland deleted the im/discussion-update-posts-linkage branch May 13, 2026 10:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[2.x] Change title of an discussion : no notification and no message

1 participant