やること
下記を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だったらバッジなし