メッセージをリアルタイムで送りあうことができるようにします。
コマンド
rails g channel Message
ルートの設定をします。
記述追加 config\routes.rb
6行目に「mount ActionCable.server => '/cable'」の記述を追加しています。
Rails.application.routes.draw do # ルートを app\views\pages\home.html.erb に設定 root 'pages#home' mount ActionCable.server => '/cable' devise_for :users, path: '', path_names: {sign_up: 'register', sign_in: 'login', edit: 'profile', sign_out: 'logout'}, controllers: {omniauth_callbacks: 'omniauth_callbacks', registrations: 'registrations'} get 'pages/home' get '/dashboard', to: 'users#dashboard' get '/users/:id', to: 'users#show', as: 'user' get '/your_trips' => 'reservations#your_trips' get '/your_reservations' => 'reservations#your_reservations' get 'search' => 'pages#search' get 'settings/payment', to: 'users#payment', as: 'settings_payment' get 'settings/payout', to: 'users#payout', as: 'settings_payout' get '/conversations', to: 'conversations#list', as: "conversations" get '/conversations/:id', to: 'conversations#show', as: "conversation_detail" post '/users/edit', to: 'users#update' post '/settings/payment', to: 'users#update_payment', as: "update_payment" post 'messages', to: 'messages#create' resources :rooms, except: [:edit] do member do get 'listing' get 'pricing' get 'description' get 'photo_upload' get 'amenities' get 'location' get 'preload' get 'preview' delete :delete_photo post :upload_photo end resources :reservations, only: [:create] end resources :guest_reviews, only: [:create, :destroy] resources :host_reviews, only: [:create, :destroy] resources :reservations, only: [:approve, :decline] do member do post '/approve' => "reservations#approve" post '/decline' => "reservations#decline" end end # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html end
「app\channels\message_channel.rb」ファイルの記述を以下のように変更します。
記述変更 app\channels\message_channel.rb
class MessageChannel < ApplicationCable::Channel def subscribed conversation = Conversation.find params[:conversation] stream_for conversation end end
「app\controllers\messages_controller.rb」ファイルに記述を追加します。
1.18行目から22行目の記述を以下の記述に置き換えます。
if @message.save conversation.update!(updated_at: @message.created_at) receiver = conversation.sender.id == current_user.id ? conversation.receiver : conversation.sender MessageChannel.broadcast_to conversation, sender_id: current_user.id, sender: render_message(@message, current_user), receiver: render_message(@message, receiver) if URI(request.referrer).path == conversation_detail_path(id: receiver.id) return render json: {success: true} end redirect_to request.referrer, notice: "メッセージを送りました" else redirect_to request.referrer, alert: "メッセージを送れませんでした" end
2.39行目にプライベートメソッド「render_message()」を追加します。
def render_message(message, user) self.render_to_string partial: 'conversations/message', locals: {m: message, user: user} end
記述追加 app\controllers\messages_controller.rb
class MessagesController < ApplicationController def create if current_user.id == message_params[:receiver_id] redirect_to request.referrer, alert: "自分にメッセージを送ることはできません" end conversation = Conversation.where("(sender_id = ? AND receiver_id = ?) OR (sender_id = ? AND receiver_id = ?)", current_user.id, message_params[:receiver_id], message_params[:receiver_id], current_user.id ).first if !conversation.present? conversation = Conversation.create(sender_id: current_user.id, receiver_id: message_params[:receiver_id]) end @message = Message.new(user_id: current_user.id, conversation_id: conversation.id, content: message_params[:content] ) if @message.save conversation.update!(updated_at: @message.created_at) receiver = conversation.sender.id == current_user.id ? conversation.receiver : conversation.sender MessageChannel.broadcast_to conversation, sender_id: current_user.id, sender: render_message(@message, current_user), receiver: render_message(@message, receiver) if URI(request.referrer).path == conversation_detail_path(id: receiver.id) return render json: {success: true} end redirect_to request.referrer, notice: "メッセージを送りました" else redirect_to request.referrer, alert: "メッセージを送れませんでした" end end private def message_params params.require(:message).permit(:content, :receiver_id) end def render_message(message, user) self.render_to_string partial: 'conversations/message', locals: {m: message, user: user} end end
「app\javascript\channels\message_channel.js」ファイルを以下のように編集します。
記述編集 app\javascript\channels\message_channel.js
import consumer from "./consumer" $(document).on('turbolinks:load', () => { $('[data-channel-subscribe="conversation"]').each(function(index, element) { var $element = $(element), $chatList = $('#message_list'), $form = $('#new_message'), conversation_id = $element.data('conversation-id'), user_id = $element.data('user-id') consumer.subscriptions.create( { channel: "MessageChannel", conversation: conversation_id }, { received: function(data) { if (data.sender_id == user_id) { $chatList.append(data.sender) } else { $chatList.append(data.receiver) } $form[0].reset(); $chatList.animate({ scrollTop: $chatList.prop("scrollHeight") }, 1000) } } ) }); });
ブラウザ確認
http://localhost:3000/conversations
メッセージを送るとリアルタイムで表示できるようになりました。