Трансляция в турбо-поток пользователя с использованием рельсов Hotwire

#ruby-on-rails #actioncable #hotwire-rails

#ruby-on-rails #actioncable #горячая проволока-рельсы

Вопрос:

Я хочу транслировать обновления модели с помощью Hotwire в Rails. В документации упоминается создание потока с использованием модели, но я хочу создать динамический поток для каждого пользователя, чтобы изменения в моделях, сделанные пользователем, передавались только этому пользователю.

У меня есть модель видеоконференции, которая имеет и принадлежит многим пользователям через объединяющую таблицу, поэтому модель выглядит следующим образом, и у меня есть трансляция как таковая

 class VideoCall < ApplicationRecord
  has_and_belongs_to_many :users

  broadcasts_to ->(video_call) {
    video_call.users.map{|u| "video_calls:user_#{u.id}"}
  }, inserts_by: :prepend

  # more code here
end
 

В шаблоне ERB, где я хочу получать обновления для модели, которую я добавил

 <%= turbo_stream_from "video_calls:user_#{current_user.id}" %>
 

Это работает, если у меня есть один пользователь в таблице users_video_calls. Но как только в video_call появляется несколько пользователей, он не работает, и я хочу транслировать в поток всех этих пользователей.

Является ли подход, который я использую, правильным и как я могу добиться динамической трансляции потоков нескольким пользователям.

Ответ №1:

Итак, я решил это следующим образом. Глядя на исходный код, я понял, что имеет смысл переопределить три обратных вызова, которые вызываются при создании, обновлении и уничтожении. Итак, я удалил этот код

   broadcasts_to ->(video_call) {
    video_call.users.map{|u| "video_calls:user_#{u.id}"}
  }, inserts_by: :prepend
 

и заменил его этим

   after_create_commit  -> {
    self.users.each do |u|
      broadcast_action_later_to "video_calls:users_#{u.id}", action: :prepend, target: broadcast_target_default
    end
  }

  after_update_commit  -> {
    self.users.each do |u|
      broadcast_replace_later_to "video_calls:users_#{u.id}"
    end
  }

  after_destroy_commit -> {
    self.users.each do |u|
      broadcast_remove_to "video_calls:users_#{u.id}", action: :prepend, target: broadcast_target_default
    end
  }

 

В приведенном выше коде теперь я могу перебирать каждую связанную модель, которая в моем случае — это много пользователей на видеозвонок, и транслировать на имя канала, содержащее идентификатор пользователя. Таким образом, все пользователи, подписавшиеся на видеозвонок, получат только обновление, а не все.

Комментарии:

1. Выглядит хорошо. Но я не думаю, что вам нужно action: :prepend в вашем broadcast_remove_to заявлении.

2. Я давно хотел сделать то же самое — использовать Hotwire для управления списком сообщений, ограниченным для вошедшего в систему пользователя. Спасибо за доработку вашего решения.

3. Спасибо, хорошее решение. Однако after_destroy_commit не работает, если вы хотите обновить представление одного и того же пользователя (допустим, веб-сайт открыт в 2 разных браузерах). Причина в том, что self.users не содержит пользователя, который только что удалил все, что есть self (в моем случае это ссылки). Я не уверен, как это сделать. Есть идеи?

4. Вы можете извлечь пользователей перед вызовом destroy.