前回までで、基本的なチャット機能は完成しました。
その他、一般的なチャットの機能として以下のようなものがあります。
これから2回に分けて上記機能を実装します。
今回はボードを見ているユーザーの表示を行います。
本テーマは7記事構成になっています。
7記事それぞれで取り扱う内容は以下です。
本テーマは以下の方針で記述します。
完成版の画面イメージは以下のようになっています。
アクティブユーザーとは、チャットボードに参加しているユーザーの事です。
これを表示する機能を追加します。
Phoenix.Presence を使用するため、まずこれを追加します。
追加するには、まずmixコマンドを使用します。
$ mix phx.gen.presence
コマンドの出力メッセージで案内されたように、application.ex
に LvChatWeb.Presence
を追加します。
# /lib/lv_chat/application.ex
children = [
# Start the Ecto repository
LvChat.Repo,
# Start the endpoint when the application starts
LvChatWeb.Endpoint,
# Starts a worker by calling: LvChat.Worker.start_link(arg)
# {LvChat.Worker, arg},
LvChatWeb.Presence]
これで準備は整いました。
Presence機能を使いやすくするために、Presenceのヘルパー関数を定義します。
関数 | 役割り |
---|---|
track_presence | Presence.trackへの転送関数 |
list_presences | 指定したトピックのリストを取り出しやすい形に変換して取得 |
extract_metadata | metasからデータを取り出す |
実装は以下のようになります。
# /lib/lv_chat_web/channels/presence.ex
alias LvChatWeb.Presence
def track_presence(pid, topic, key, payload) do
Presence.track(pid, topic, key, payload)
end
def list_presences(topic) do
topic
|> Presence.list
|> Enum.map(fn {_user_id, data} ->
data
|> extract_metadata
end)
end
defp extract_metadata(data) do
data
|> Map.get(:metas)
|> List.first
end
extract_metadata
についてですが、もとのデータは以下のような形で取得されます。
%{metas: [%{id: 1, name: "freddie", phx_ref: "OoxOA/GE9hY="}]}
変換後は以下のようになります。
%{id: 1, name: "freddie", phx_ref: "OoxOA/GE9hY="}
複数のユーザーがそのチャットボードにアクセスしているときは、リストとして取得されます。
[
%{id: 1, name: "freddie", phx_ref: "OoxOA/GE9hY="},
%{id: 2, name: "brian", phx_ref: "aUeSKX6opX0="}
]
mount時に以下の事を行います。
ハイライト箇所が今回追加したところです。
# /lib/lv_chat_web/views/board_live_view.ex
def mount(session = %{current_user: user, board: board}, socket) do
Presence.track_presence( self(), board.id |> topicId, user.id, %{ name: user.name, id: user.id, } )
assigns =
Map.merge(
session,
%{
comments: board |> Meeting.list_comments,
comment: LvChat.Meeting.change_comment,
users: session.board.id |> topicId |> Presence.list_presences() }
)
session.board.id
|> topicId
|> LvChatWeb.Endpoint.subscribe
{:ok, assign(socket, assigns)}
end
追跡を開始すると、追跡情報の変化が生じる度に presence_diff
イベントが送られてきます。
そのイベントに対応し、現在の追跡情報をsocketに指定します。
今回は、:users
に追跡されている全ユーザーを指定しています。
def handle_info(%{event: "presence_diff"}, socket = %{assigns: %{board: board}}) do
{:noreply, assign(socket, users: board.id |> topicId |> Presence.list_presences)}
end
アクティブユーザーを表示するコードは以下になります。
ハイライトされている箇所でユーザーを表示しています。
データの区別のために、 id
ではフレームワーク側で付与している phx_ref
を使用します。
# /lib/lv_chat_web/templates/board/show.html.leex
<h3>Users</h3>
<ul id="user-container">
<%= for user <- @users do %>
<li id="<%= user.phx_ref %>"><%= user.name %></li> <% end %>
</ul>
これでアクティブユーザーの表示ができるようになりました。
今回は、アクティブユーザーの表示を行いました。
Phoenixでは、リアルタイムステータスの更新はPresenceを使って実現します。
リアルタイムステータスとしては、今回のアクティブユーザーの他にタイピング状況があります。
次回では、その機能を実装します。