Ensure turbo_frame_tag requests src after turbo_stream_from is connected

I decided to give the server implementation one more chance and I think I found a possible solution that I don’t hate that should be quite easy to reuse for other methods like this.

<%= turbo_stream_from [current_user, :campaign_list_items] %>

<% @campaigns.each do |campaign| %>
  <%= turbo_frame_tag dom_id(campaign), src: campaign_list_items_path(campaign.id) do %>
    <div class="grid grid-cols-7 p-3 bt"> </div>
  <% end %>
</div>
class RetriableBroadcaster
  def initialize(broadcast_method, *streamables, **options)
    @broadcast_method = broadcast_method
    @streamables = streamables
    @options = options
  end

  def call(attempts = 0)
    return broadcast if client_connected?
    return unless attempts <= max_retries

    attempts += 1
    sleep wait_for
    call(attempts)
  end

  private

  def broadcast
    @streamables.first.public_send(
      @broadcast_method,
      *@streamables,
      **@options.except(:max_retries, :wait_for)
    )
  end

  def wait_for
    @options[:wait_for].presence || 0.1
  end

  def max_retries
    @options[:max_retries].presence || 5
  end

  def client_connected?
    redis.pubsub("channels", verified_stream_name).present?
  end

  def redis
    @redis ||= ActionCable.server.pubsub.redis_connection_for_subscriptions
  end

  def signed_stream_name
    Turbo::StreamsChannel.signed_stream_name(@streamables)
  end

  def verified_stream_name
    Turbo::StreamsChannel.verified_stream_name(signed_stream_name)
  end
end
module Campaigns
  class ListItemStatsWorker
    include Sidekiq::Worker
    sidekiq_options queue: :within_30_seconds, retry: 0

    def perform(campaign_id)
      return unless (campaign = Campaign.find(campaign_id))

      RetriableBroadcaster.new(
        :broadcast_update_to,
        campaign.user,
        :campaign_list_items,
        target: "campaign_#{campaign.id}",
        partial: "dashboard/campaigns/list_items/list_item",
        locals: {
          campaign: campaign, stats: CampaignStatsPresenter.new(campaign, nil, nil)
        },
        max_retries: 5
      ).call
    end
  end
end

Ideally we could build this into source and provide a signature more like

user.broadcast_update_to(
  campaign.user, 
  :campaign_list_items, 
  target: "target_name", 
  partial: "partial_path", 
  max_retries: 5
)