Solution
Putting this at the end of my API method solved the problem:
MessageBus.publish("/topic/#{@topic.id}", reload_topic: true, refresh_stream: true)
This should refresh not only for the current user, but anyone else viewing the topic.
Exploration that led me here
Read on if you want to track with me through the code base. I share this for anyone else it may be useful for.
What is MessageBus?
From Sam’s 2013 explanation of the MessageBus it seems like the Ruby code can publish and subscribe, whereas the Javascript code can only subscribe. Ok, so under this proposed architecture my server side code will be responsible for notifying the UI.
Is there a MessageBus example that is useful to me?
None of the plugin examples do anything with MessageBus.
GroupArchivedMessage.move_to_inbox! calls this code which is somewhat close to what I want, but the type code used here won’t work for me.
MessageBus.publish("/topic/#{topic_id}", { type: "move_to_inbox" }, group_ids: [group_id])
This code looks promising.
MessageBus.publish("/topic/#{@topic.id}", reload_topic: true, refresh_stream: true)
This was the final solution I chose.
What happens when the browser receives the update?
The Topics controller is subscribed to updates on the topic it is presently viewing. It triggers “post-stream:refresh”, which initiates the refresh. The only thing I can find listening on the other end is the ScrollingPostStream which binds a _refresh method to the event. This seems to be what’s actually performing the update.
By this I infer that if I wanted to just update the present client without pushing the update to anyone else, I could call the same code myself:
this.appEvents.trigger("post-stream:refresh", args)
And it looks like I could call this from any component.
What about the original implementation of move_posts? Does it publish to MessageBus?
Investigating core I see: The topic controller move_posts() method calls move_posts_to_destination() which calls topic.move_posts() which calls new PostMover() and then either to_topic() or to_new_topic(). These methods don’t call MessageBus, but they do call DiscourseEvent.trigger().
I call into topic.move_posts() from my custom API method. So my code calls directly into the model, and skips any of the existing controller logic:
new_topic = topic.move_posts(current_user, [post.id], {title: title, category_id: category_id})
In my case move_posts_to() gets called as the final step in the sequence, which calls the following:
DiscourseEvent.trigger(
:posts_moved,
destination_topic_id: destination_topic.id,
original_topic_id: original_topic.id,
)
DiscourseEvent doesn’t seem to have anything to do with MessageBus, however. Maybe it’s only used for internal lifecycles within the server?
So I’m concluding that the MessageBus is not normally published to for a topic move.