Background 

In an application I worked on, we had a scheduling screen displaying scheduled and unscheduled jobs. Each user could see a slightly different list of jobs based on their applied filters. For instance, User 1 might see all jobs, while User 2 only saw small jobs.

The screen was an index page with two frames, one for scheduled jobs and another for unscheduled jobs. My goal was to ensure that if User 1 scheduled a job, User 2's view would update accordingly and vice versa. Since users were viewing slightly different lists, jobs needed to "move" between the scheduled and unscheduled sections. This made it too complex to manage with standard stream actions like append, remove, or replace.

While I could have implemented polling to refresh the views, I decided to explore a solution where the front end would be notified when a full refresh was needed.

Implementation 

Below is the outer index page containing two frames. The search_session_token parameter is used to track the filters applied by each user, so the token varies between User 1 and User 2. The frames load two tables: one for scheduled jobs and another for unallocated jobs. These tables include menu options for actions like scheduling and unscheduling jobs.
<div class="flex flex-col space-y-4">
  <div>
    <%= turbo_stream_from :scheduled_jobs_container_stream %>
    <%= turbo_frame_tag :scheduled_jobs_container, src: scheduled_jobs_path(search_session_token: search_seassion_token), data: {controller: "session-frame-refresh"} do %>
    <% end %>  
  </div>

  <div>
    <%= turbo_stream_from :unallocated_jobs_container_stream %>
    <%= turbo_frame_tag :unallocated_jobs_container, src: unallocated_jobs_path(search_session_token: search_seassion_token), data: {controller: "session-frame-refresh"} do %>
    <% end %>  
  </div>  
</div>
If User 1 schedules a job, I wanted those changes to be "broadcast" to other users. As noted earlier, the jobs visible to User 1 and User 2 might differ, and User 2 might not even have that particular job in their view. To keep things simple, I decided the best approach was to refresh User 2's data using the filters they already had applied.
To implement this, I created a small Stimulus controller (shown below) that calls the turbo frame method reload(). This behaviour is handled by the refresh() method in the controller.
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = ["refresh"] 
  connect() {
  }

  refreshTargetConnected(element) {
    this.refresh();
    element.remove();
  }

  refresh() {
    this.element.reload();
  }
}
You can see above I connected this controller to each of the frames. 

Next, I needed a way to trigger the refresh() method when something changed on the server. To achieve this, I broadcasted an append action to the frame, adding a <div> with a data-target attribute set to "refresh." When this div is appended, the refreshTargetConnected method is automatically invoked, allowing me to call refresh() and trigger the update.

In my code, I created a simple concern that can be called from the necessary places to trigger the refresh. The implementation is shown below.
module Broadcaster::Jobs
  extend ActiveSupport::Concern
  include ActionView::Helpers::TagHelper

  def handle_broadcasts
    # Trigger a refresh of the scheduled jobs, unallocated jobs
    %w[scheduled_jobs_container unallocated_jobs_container].each do |target|
      Turbo::StreamsChannel.broadcast_append_to "#{target}_stream",
        target: target,
        html: content_tag(:div, "", data: {session_frame_refresh_target: "refresh"}),
        formats: [:turbo_stream]
    end
  end
end