5
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

railsでメッセージ機能と未読管理を実装する

Posted at

やること

下記をrailsで実装していく。(ちなみにviewはtailwindcssを使っている

  • メッセージを1対1で送る
  • 未読管理をする

メッセージ機能

仕様は下記

  • 人に対してDM
  • 1対1のみでグループでのdmなどはない
  • メッセージ一覧ページとメッセージルームがある
  • 一覧ページで未読のものはその未読数のバッジがつく

scheme

message_table

  • id:integer
  • content:text
  • user_id:integer
  • to_user_id:integer
  • to_user_opentime:timestamp

roomテーブルを設けるか迷ったがグループDMをつける予定はないので1対1のみを想定し、シンプルに。

to_user_opentimeで開封した日時を入れる。これがnilだと未読ということに

routes

config/routes.rb
  get 'messages/index/:id' => "messages#index"
  get 'messages/room/:user_id/:to_user_id' => "messages#roomshow"
  resources :messages

controller

app/controllers/messages_controller.rb
class MessagesController < ApplicationController
  def index
    # 送られたユーザーでないと表示できないように
    if current_user.id.to_i == params[:id].to_i
      @user = User.find_by(id: params[:id])
    # messageをしているユーザーのidを配列で取得その後に自分のは削除。これで一覧でどのユーザーに対してのDMかを表示させる
      @message_user_ids = Message.where(to_user_id: @user.id).or(Message.where(user_id: @user.id)).distinct.pluck(:user_id)
      @message_user_ids.delete(@user.id)
    else
      flash[:notice] = "権限がありません"
      redirect_to("/")
    end
  end
  def roomshow
    if current_user.id.to_i == params[:user_id].to_i
      @to_user_id = params[:to_user_id]
      @messages = Message.where(user_id: params[:user_id],to_user_id: @to_user_id).or(Message.where(user_id: params[:to_user_id],to_user_id: params[:user_id])).order(created_at: :asc)
      unread_messages = Message.where(to_user_opentime: nil,to_user_id: current_user.id)
      unread_messages.each do |unread_message|
        unread_message.to_user_opentime = Date.today.to_time
        unread_message.save
      end
    else
      flash[:notice] = "権限がありません"
      redirect_to("/")
    end
  end
  def create
    if current_user.id.to_i == params[:user_id].to_i
      message = Message.new(content: params[:content],user_id: params[:user_id],to_user_id: params[:to_user_id])
      if message.save
        flash[:notice] = "送信しました!"
        redirect_back(fallback_location: root_path)
      else
        redirect_to("/")
        flash[:alert] = "投稿できませんでした"
      end
    end
  end
end

view

app/views/messages/index.html.erb
  <%
  @message_user_ids.each do |message_user_id|
  message_user = User.find_by(id: message_user_id.user_id)
  %>
  <div class="border-gray-400 flex flex-row mb-2 bg-white">
    <div class="flex flex-col w-10 h-10 justify-center items-center mr-4">
      <%= image_tag('default_icon.png',class: "mx-auto object-cover rounded-full h-10 w-10",alt: "ユーザーアイコン") %>
    </div>
    <div class="">
      <%= message_user.name %>
    </div>
  </div>
  <% end %>

上記で@message_user_idsで取得した自分以外のメッセージしているユーザーのidを使って一覧表示

app/views/messages/roomshow.html.erb

<div class="flex flex-col">
  <%
  @messages.each do |message|
  %>
    <% if message.to_user_id == current_user.id %>
    <div class="flex flex-row mb-8">
      <div class="flex items-center justify-center h-10 w-10 rounded-full bg-white flex-shrink-0">
        <%= image_tag('default_icon.png',class: "mx-auto object-cover rounded-full h-10 w-10",alt: "ユーザーアイコン") %>
      </div>
      <div class="ml-3 text-sm bg-white py-2 px-4 shadow rounded-xl">
        <p>
          <%= message.content %>
        </p>
      </div>
    </div>
    <% else %>
    <div class="flex flex-row-reverse mb-8">
      <div class="flex items-center justify-center h-10 w-10 rounded-full bg-white flex-shrink-0">
        <%= image_tag('default_icon.png',class: "mx-auto object-cover rounded-full h-10 w-10",alt: "ユーザーアイコン") %>
      </div>
      <div class="ml-3 text-sm bg-white py-2 px-4 shadow rounded-xl">
        <p>
          <%= message.content %>
        </p>
      </div>
    </div>
    <% end %>
  <% end %>
</div>
<%= form_with model: @message, url: messages_path do |form| %>
<div class="">
  <%= form.text_area :content, id: "content", rows: "4", class: "form_textarea mb-4" %>
</div>
<%= form.text_area :user_id, class: "hidden", value: current_user.id %>
<%= form.text_area :to_user_id, class: "hidden", value: @to_user_id %>
<%= form.submit "メッセージ送信", class: "btn_01" %>
<% end %>

dmページでは自分へのメッセージと自分が送ったメッセージを振り分けて左右対象になるように繰り返し処理を行っている。

メッセージ未読管理

未読管理でやることは下記

  • current_userに対して送信されているdmで未読のものをカウント
  • 読んだらnilのものだけ取得してto_user_opentimeに日時を入れる
  • 未読と判断するのはto_user_opentimeがnilのもの

controller

app/controllers/messages_controller.rb
      unread_messages = Message.where(to_user_opentime: nil,to_user_id: current_user.id)
      unread_messages.each do |unread_message|
        unread_message.to_user_opentime = Date.today.to_time
        unread_message.save
      end

上記をroom表示の際に記載。

  • unread_messagesにnilのものを配列で入れる
  • eachでto_user_opentimeに現在時刻を入れる

view

app/views/messages/index.html.erb
 <%
  @message_user_ids.each do |message_user_id|
  message_user = User.find_by(id: message_user_id)
  unread_count = Message.where(user_id: message_user_id,to_user_id: @user.id,to_user_opentime: nil).count
  %>
  <a href="/messages/room/<%= current_user.id %>/<%= message_user.id %>">
  <div class="border-b border-gray-200 flex py-4 bg-white items-center">
    <div class="w-10 h-10 mr-4">
      <img src="<%= user_icon_url(message_user) %>" class="object-cover rounded-full h-10 w-10"/>
    </div>
    <div class="">
      <%= message_user.name %>
    </div>
    <% if unread_count > 0 %>
    <div class="rounded-full bg-green-300 ml-4 text-white w-6 h-6 text-center">
      <%= unread_count %>
    </div>
    <% end %>
  </div>
  </a>
  <% end %>
  • unread_countを取得し、0以上だったら表示

headerに未読バッジをつける

app/controllers/application_controller.rb
  before_action :message_notification
  def message_notification
    @message_notification_count = Message.where(to_user_id: current_user.id,to_user_opentime: nil).count
  end

application_controller.rbで未読数を取得

view

app/views/layouts/_header.html.erb
      <a href="/messages/index/<%= current_user.id %>">
      <div class="mr-2">
        <% if @message_notification_count > 0 %>
          <%= image_tag('message_icon_unread.png',class: "header_icon align-middle",alt: "airteam") %>
        <% else %>
          <%= image_tag('message.png',class: "header_icon align-middle",alt: "airteam") %>
        <% end %>
      </div>
      </a>
  • 未読数が0より上だったらバッジ付きの画像アイコンで0だったらバッジなし
5
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?