feat(chat): show unread message counter

This commit is contained in:
João Paulo Dubas 2024-12-19 00:40:24 +00:00
parent 33397055bb
commit f5704fa13c
Signed by: joao.dubas
SSH Key Fingerprint: SHA256:V1mixgOGRc/YMhGx/DNkOSmJxgA2vHNrDZEk3wt/kOA
3 changed files with 83 additions and 28 deletions

View File

@ -91,11 +91,19 @@ defmodule Slax.Chat do
Repo.all(query)
end
def list_joined_rooms(%User{} = user) do
user
|> Repo.preload(:rooms)
|> Map.fetch!(:rooms)
|> Enum.sort_by(& &1.name)
def list_joined_rooms_with_unread_count(%User{} = user) do
query =
from(room in Room,
join: membership in assoc(room, :memberships),
left_join: message in assoc(room, :messages),
on: message.id > membership.last_read_id,
where: membership.user_id == ^user.id,
group_by: room.id,
select: %{room | unread_message_count: count(message.id)},
order_by: [asc: room.name]
)
Repo.all(query)
end
def list_rooms_with_joined(%User{} = user) do

View File

@ -8,9 +8,11 @@ defmodule Slax.Chat.Room do
schema "rooms" do
field :name, :string
field :topic, :string
field :unread_message_count, :integer, virtual: true, default: 0
many_to_many :members, User, join_through: RoomMembership
has_many :memberships, RoomMembership
has_many :messages, Message
timestamps(type: :utc_datetime)

View File

@ -63,7 +63,7 @@ defmodule SlaxWeb.ChatRoomLive do
<div class="flex justify-between items-center flex-shrink-0 h-16 bg-white border-b border-slate-300 px-4">
<div class="flex flex-col gap-1.5">
<h1 class="text-sm font-bold leading-none">
#<%= @room.name %>
#{@room.name}
<.link
:if={@joined?}
class="font-normal text-xs text-blue-600 hover:text-blue-700"
@ -76,14 +76,14 @@ defmodule SlaxWeb.ChatRoomLive do
<%= if @hide_topic? do %>
<span class="text-slate-600">[Topic hidden]</span>
<% else %>
<%= @room.topic %>
{@room.topic}
<% end %>
</div>
</div>
<ul class="relative z-10 flex items-center gap-4 px-4 sm:px-6 lg:px-8 justify-end">
<%= if @current_user do %>
<li class="text-[0.8125rem] leading-6 text-zinc-900">
<%= username(@current_user) %>
{username(@current_user)}
</li>
<li>
<.link
@ -173,8 +173,8 @@ defmodule SlaxWeb.ChatRoomLive do
>
<div class="max-w-3-xl text-center">
<div class="mb-4">
<h1 class="text-xl font-semibold"><%= @room.name %></h1>
<p :if={@room.topic} class="text-sm mt-1 text-gray-600"><%= @room.topic %></p>
<h1 class="text-xl font-semibold">{@room.name}</h1>
<p :if={@room.topic} class="text-sm mt-1 text-gray-600">{@room.topic}</p>
</div>
<div class="flex items-center justify-around">
<buttom
@ -201,7 +201,7 @@ defmodule SlaxWeb.ChatRoomLive do
@impl Phoenix.LiveView
def mount(_params, _session, socket) do
rooms = Chat.list_joined_rooms(socket.assigns.current_user)
rooms = Chat.list_joined_rooms_with_unread_count(socket.assigns.current_user)
users = Accounts.list_users()
timezone = get_connect_params(socket)["timezone"]
@ -212,6 +212,8 @@ defmodule SlaxWeb.ChatRoomLive do
OnlineUsers.subscribe()
Enum.each(rooms, &Chat.subscribe_to_room/1)
socket =
socket
|> assign(
@ -233,8 +235,6 @@ defmodule SlaxWeb.ChatRoomLive do
@impl Phoenix.LiveView
def handle_params(params, _session, socket) do
if socket.assigns[:room], do: Chat.unsubscribe_from_room(socket.assigns.room)
room =
case Map.fetch(params, "id") do
{:ok, id} ->
@ -248,8 +248,6 @@ defmodule SlaxWeb.ChatRoomLive do
messages = room |> Chat.list_messages_in_room() |> maybe_insert_unread_marker(last_read_id)
Chat.subscribe_to_room(room)
Chat.update_last_read_id(room, socket.assigns.current_user)
{:noreply,
@ -262,7 +260,18 @@ defmodule SlaxWeb.ChatRoomLive do
)
|> stream(:messages, messages, reset: true)
|> assign_message_form(Chat.change_message(%Message{}))
|> push_event("scroll_messages_to_bottom", %{})}
|> push_event("scroll_messages_to_bottom", %{})
|> update(:rooms, fn rooms ->
room_id = room.id
Enum.map(rooms, fn
%Room{id: ^room_id} = room ->
%Room{room | unread_message_count: 0}
other_room ->
other_room
end)
end)}
end
@impl Phoenix.LiveView
@ -309,20 +318,42 @@ defmodule SlaxWeb.ChatRoomLive do
room = socket.assigns.room
Chat.join_room!(room, current_user)
Chat.subscribe_to_room(room)
socket = assign(socket, joined?: true, rooms: Chat.list_joined_rooms(current_user))
socket =
assign(socket, joined?: true, rooms: Chat.list_joined_rooms_with_unread_count(current_user))
{:noreply, socket}
end
@impl Phoenix.LiveView
def handle_info({:new_message, message}, socket) do
if message.room_id == socket.assigns.room.id do
Chat.update_last_read_id(message.room, socket.assigns.current_user)
end
room = socket.assigns.room
socket =
socket
|> stream_insert(:messages, message)
|> push_event("scroll_messages_to_bottom", %{})
cond do
message.room_id == room.id ->
Chat.update_last_read_id(room, socket.assigns.current_user)
socket
|> stream_insert(:messages, message)
|> push_event("scroll_messages_to_bottom", %{})
message.user_id != socket.assigns.current_user.id ->
update(socket, :rooms, fn rooms ->
room_id = message.room_id
Enum.map(rooms, fn
%Room{id: ^room_id} = room ->
%Room{room | unread_message_count: room.unread_message_count + 1}
other_room ->
other_room
end)
end)
true ->
socket
end
{:noreply, socket}
end
@ -353,12 +384,26 @@ defmodule SlaxWeb.ChatRoomLive do
>
<.icon name="hero-hashtag" class="h-4 w-4" />
<span class={["ml-2 leading-none", @active && "font-bold"]}>
<%= @room.name %>
{@room.name}
</span>
<.unread_message_count count={@room.unread_message_count} />
</.link>
"""
end
attr :count, :integer, required: true
defp unread_message_count(assigns) do
~H"""
<span
:if={@count > 0}
class="flex items-center justifiy-center bg-blue-500 rounded-full font-medium h-5 px-2 ml-auto text-xs text-white"
>
{@count}
</span>
"""
end
attr :user, User, required: true
attr :online, :boolean, default: false
@ -372,7 +417,7 @@ defmodule SlaxWeb.ChatRoomLive do
<span class="w-2 h-2 rounded-full border-2 border-gray-500"></span>
<% end %>
</div>
<span class="ml-2 leading-none"><%= username(@user) %></span>
<span class="ml-2 leading-none">{username(@user)}</span>
</.link>
"""
end
@ -398,12 +443,12 @@ defmodule SlaxWeb.ChatRoomLive do
<div class="ml-2">
<div class="-mt-1">
<.link class="text-sm font-semibold hover:underline">
<span><%= username(@message.user) %></span>
<span>{username(@message.user)}</span>
</.link>
<span :if={@timezone} class="ml-1 text-xs text-gray-500">
<%= message_timestamp(@message, @timezone) %>
{message_timestamp(@message, @timezone)}
</span>
<p class="text-sm"><%= @message.body %></p>
<p class="text-sm">{@message.body}</p>
</div>
</div>
</div>