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

# やること

下記を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

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

## controller

```ruby: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

```ruby: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を使って一覧表示

```ruby: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

```ruby: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

```ruby: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に未読バッジをつける

```ruby: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

```ruby: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だったらバッジなし