はしばみあきら blog

プログラミングアウトプットするブログ。202010スタート

【RailsでLINEを作る】ActionCableで特定のユーザーとリアルタイムチャット機能を作る.その2

前回はアクションケーブルを使ったリアルタイムチャット機能を実装しました。

ただ、今の状態だと、送ったチャットが、同じチャンネルに接続されている全てのviewに配信されます。

ユーザーidやルームidは保存されるのでリロードを挟めば消えるのですが、かといってダダ漏れでは大変です。

こんな感じです。

これをちゃんと同じルームの相手にだけリアルタイムで配信するようにします。

前回のコードに追記していきます。

特定のユーザーとリアルタイムチャット機能

今回こちらのかたの記事を参考にさせて頂き実装しました。

Rails 5 Action Cable メッセージとルームを紐付ける。

チャットを送るinputタグにはユーザーidとトークルームidをつけてあります。

この部分ですね。

talk_rooms/show.html.slim

    input[type="text"
          data-behavior="room_speaker"
          data-user="#{current_user.id}"
          data-room="#{@room.id}"]

このinputにidを持たせます。

talk_rooms/show.html.slim

    input[type="text"
          id="talk-form"
          data-behavior="room_speaker"
          data-user="#{current_user.id}"
          data-room="#{@room.id}"]

この値はjs(coffee)に渡され、さらにチャンネルまで渡します。

javascript/channels/talk_room.coffee

$ ->
  talkForm = $('#talk-form')

  App.talk_room = App.cable.subscriptions.create { channel: "TalkRoomChannel", room_id: talkForm.data('room')},

    # 通信が確立された時
    connected: ->

    # 通信が切断された時
    disconnected: ->

    # 値を受け取った時
    received: (data) ->
      # サーバーサイドから値を受け取りviewに追加する
      $('#talk').append("<p>"+data["talk"]+"</p>");

    speak: (talk) ->
      # サーバーサイド(channel)のspeakアクションにtalkパラメータを渡す
      @perform 'speak', talk: talk

  $(document).on 'keypress', '[data-behavior~=room_speaker]', (event) ->
    # return(Enter)が押された時
    if event.keyCode is 13
      # channel speakへ, event.target.valueを引数に
      App.talk_room.speak [event.target.value, $('[data-user]').attr('data-user'), $('[data-room]').attr('data-room')]
      event.target.value = ''
      event.preventDefault()

少し沼ったのですが、coffeeはインデントで閉じる判定をしている?のか、 App.talk_room = App... の部分をインデント空けずに記述したら変数を渡せていませんでした。インデント注意。

これでchannelにroom_idを流せるので、channelに記述していきます。

channels/talk_room_channnel.rb

class TalkRoomChannel < ApplicationCable::Channel

  # 接続された時
  def subscribed
    # フロントとバックが通信している時(お互いを監視している時)に実行される
    stream_from "talk_room_channel_#{params['room_id']}"
  end

  # 切断された時
  def unsubscribed
  end

  def speak(talk)
    # Talkモデル内、talkカラムに、(talk)に渡されたvalue['文字列']が保存される
    talk = Talk.new(talk: talk['talk'][0],
                    user_id: talk['talk'][1].to_i,
                    talk_room_id: talk['talk'][2].to_i)
    talk.save
    # 同じchannel名の全てにインプットタグに入力された文字を配信する
    ActionCable.server.broadcast "talk_room_channel_#{params['room_id']}", talk: talk['talk']
  end

end

talk_room_channelを動的に区別できるようになりました。

これでブラウザを立ち上げてチャットをしてみます。

ちょっと見づらいですが同じルームだけチャットが届くようになりました。

これで一旦action cableを使ったリアルタイムチャット機能が完成しました。

もっと色々と応用がありそうだ・・・jobっていうのはまだ分かっておらずです。

これをLINEの見た目にしていきたいけど今はちょっと頭がそこまで回りそうにないぞ...!

この記事が何かのお役に立てれば幸いです。

またボチボチ更新していきます。