Home(マイページ)のFeedに未読情報を追加したい
Rails Tutorialで作成したTwitter機能に,未読の情報を自分のFeed (タイムライン)に表示できるようにしてみた.
方針
各Micropostにnew flagというカラムを追加することを一瞬考えたが,それだとあるユーザーが既読にしてしまうと他のユーザーにとってもそのMicropostが既読になってしまうのでだめ.
そのため方針としては,ユーザーがHomeに最後にアクセスした時間(lastaccesshome)をカラムに追加し,Micropostが更新された時間(updated_at)と比較して未読・既読を判断することにした.
やってみる
まず,usersにHomeに最後にアクセスした時間をlastaccesshomeとしてカラムに追加.
型はupdated_atと同じdatetime型にしておく.
rails generate migration AddLastaccesshomeToUsers lastaccesshome:datetime
できたファイルにdefault値も追加しておく.ここではDatetime.nowを追加した.
class AddLastaccesshomeToUsers < ActiveRecord::Migration[5.0]
def change
add_column :users, :lastaccesshome, :datetime, default:DateTime.now
end
end
問題点
このあと,実際に値を比較して未読・既読の判定を行うのだが,Rails TutorialではMicropostのFeedを表示するviewは一つにしている,つまりHomeで表示されるFeedとprofileで表示されるFeedは同じviewにrenderしている.
今回,未読情報の表示はHomeで表示されるFeedのみにしたい(と僕は思った)ので,区別して表示される必要がある.
もう一つ,未読を既読にする処理(lastaccesshomeを更新する処理)を行うメソッドをstatic_pages_controller.rbで定義するのだが,after_actionとして実行するとエラーが出てきてしまうという問題も起きた.
#解決策
上記の二つの問題を一気に解決する手軽な手段として,ユーザにカラムをもう一つ追加するという手段を考えた(あまり良くない手段かもしれない).
具体的にはアクセスしたその瞬間の時間を持つものとしてaccesstimeというカラムを追加する.
rails generate migration AddAccesstimeToUsers accesstime:datetime
class AddAccesstimeToUsers < ActiveRecord::Migration[5.0]
def change
add_column :users, :accesstime, :datetime, default:DateTime.now
end
end
これによって,
一つ目の問題に対しては,usersのcontrollerにはbefore_actionとして既読処理メソッドを初めの計画通り追加することで先にlastaccesshomeを更新して,必ず既読になるようにした.
二つ目の問題に対しては,static_pagesのcontrollerにbefore_actionとして既読処理メソッドを追加するが,accesstimeの値をlastaccesshomeに代入したあと,accesstimeの値を現在の値に更新することでbefore_actionによって最終ログインの時間が先に更新されて消えてしまうことを防ぐようにした.
コード
実際変更した部分は以下.
controller
class UsersController < ApplicationController
.
.
.
before_action :read, only: :show
.
.
.
#before action
.
.
.
#既読処理メソッド
def read
@user = current_user
@user.lastaccesshome = Time.zone.now
@user.save!
end
end
class StaticPagesController < ApplicationController
before_action:read, only: :home
.
.
.
private
#before_action
#既読処理メソッド
def read
if logged_in?
@user = current_user
@user.lastaccesshome = @user.accesstime
@user.accesstime = DateTime.now
@user.save!
end
end
end
view
未読情報を示す青丸のscss(適当に作りました)
/* microposts */
.microposts {
.
.
.
.newsign {
float: right;
border-radius: 50%;
height: 20px;
width: 20px;
// background-color: rgb(63, 59, 247);
background: skyblue;
}
.
.
.
}
次に,各micropostについてcurrent userのlastaccesshomeとmicropostのupdated_atを比較し,updated_at > lastaccesshomeの場合にのみ青丸を表示するように,app/views/micropostsにある_micropost.html.erbを変更.
<li id="micropost-<%= micropost.id %>">
<%= link_to gravatar_for(micropost.user, size: 50), micropost.user %>
<span class="user"><%= link_to micropost.user.name, micropost.user %></span>
<span class="content">
<%= micropost.content %>
<% if micropost.created_at > current_user.lastaccesshome %>
<span class="newsign"></span>
<% end %>
<%= image_tag micropost.picture.url if micropost.picture? %>
</span>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
<% if current_user?(micropost.user) %>
<%= link_to "delete", micropost, method: :delete,
data: { confirm: "You sure?" } %>
<% end %>
</span>
</li>
実行結果
実際に作ったものはこちら.
以上の変更を加えた後,herokuにデプロイして動くことを確認.
試しに新しくユーザ(hoge)を作成し,example userでそのユーザをフォローした後hogeで何かを投稿し,それがexample userのHomeで新情報として青丸がついているか確かめてみる.
example userでhogeをフォローした後,hogeのhomeで「hoge」と投稿.新しく投稿した情報として青丸で表示される.
リロードすると青丸は消える.
次にexample userでログインし,Homeにアクセスする.
example userの前回アクセスの後に投稿されたhogeの「hoge」が新規情報として表示されている.
リロードすると青丸は消える.望んでいた処理ができた.
なお,他のユーザのプロフィールにおいては青丸はついていないことも確認できる.
# 最後に
今回実装した方法よりもっといい方法がありそう.カラムを二つも追加するのはかなり冗長な気がする.
ぜひあれば教えてください.