feat(chat): add unread message divider

This commit is contained in:
João Paulo Dubas 2024-11-25 23:22:31 +00:00
parent 24c457413d
commit 2430b41ce1
Signed by: joao.dubas
SSH Key Fingerprint: SHA256:V1mixgOGRc/YMhGx/DNkOSmJxgA2vHNrDZEk3wt/kOA
4 changed files with 88 additions and 10 deletions

View File

@ -3,6 +3,7 @@ defmodule Slax.Chat do
Context to handle with chat details. Context to handle with chat details.
""" """
import Ecto.Changeset
import Ecto.Query import Ecto.Query
alias Slax.Accounts.User alias Slax.Accounts.User
@ -36,7 +37,7 @@ defmodule Slax.Chat do
end end
def toggle_room_membership(%Room{} = room, %User{} = user) do def toggle_room_membership(%Room{} = room, %User{} = user) do
case Repo.get_by(RoomMembership, room_id: room.id, user_id: user.id) do case get_membership(room, user) do
%RoomMembership{} = membership -> %RoomMembership{} = membership ->
Repo.delete(membership) Repo.delete(membership)
{room, false} {room, false}
@ -47,6 +48,31 @@ defmodule Slax.Chat do
end end
end end
def update_last_read_id(%Room{} = room, %User{} = user) do
case get_membership(room, user) do
%RoomMembership{} = membership ->
query = from(m in Message, where: m.room_id == ^room.id, select: max(m.id))
id = Repo.one(query)
membership
|> change(%{last_read_id: id})
|> Repo.update()
nil ->
nil
end
end
def get_last_read_id(%Room{} = room, %User{} = user) do
case get_membership(room, user) do
%RoomMembership{} = membership ->
membership.last_read_id
nil ->
nil
end
end
def change_room(room, attrs \\ %{}) do def change_room(room, attrs \\ %{}) do
Room.changeset(room, attrs) Room.changeset(room, attrs)
end end
@ -131,4 +157,8 @@ defmodule Slax.Chat do
defp topic(%Room{id: room_id}), do: "chat_room:#{room_id}" defp topic(%Room{id: room_id}), do: "chat_room:#{room_id}"
defp topic(room_id), do: topic(%Room{id: room_id}) defp topic(room_id), do: topic(%Room{id: room_id})
defp get_membership(%Room{} = room, %User{} = user) do
Repo.get_by(RoomMembership, room_id: room.id, user_id: user.id)
end
end end

View File

@ -6,6 +6,8 @@ defmodule Slax.Chat.RoomMembership do
alias Slax.Chat.Room alias Slax.Chat.Room
schema "room_memberships" do schema "room_memberships" do
field :last_read_id, :integer
belongs_to :room, Room belongs_to :room, Room
belongs_to :user, User belongs_to :user, User

View File

@ -128,13 +128,21 @@ defmodule SlaxWeb.ChatRoomLive do
phx-hook="RoomMessages" phx-hook="RoomMessages"
phx-update="stream" phx-update="stream"
> >
<.message <%= for {dom_id, message} <- @streams.messages do %>
:for={{dom_id, message} <- @streams.messages} <%= if message == :unread_marker do %>
current_user={@current_user} <div id={dom_id} class="w-full flex text-red-500 items-center gap-3 pr-5">
dom_id={dom_id} <div class="w-full h-px grow bg-red-500"></div>
message={message} <div class="text-sm">New</div>
timezone={@timezone} </div>
/> <% else %>
<.message
current_user={@current_user}
dom_id={dom_id}
message={message}
timezone={@timezone}
/>
<% end %>
<% end %>
</div> </div>
<div :if={@joined?} class="h-12 bg-white px-4 pb-4"> <div :if={@joined?} class="h-12 bg-white px-4 pb-4">
<.form <.form
@ -205,13 +213,20 @@ defmodule SlaxWeb.ChatRoomLive do
OnlineUsers.subscribe() OnlineUsers.subscribe()
socket = socket =
assign(socket, socket
|> assign(
hide_topic?: false, hide_topic?: false,
online_users: OnlineUsers.list(), online_users: OnlineUsers.list(),
rooms: rooms, rooms: rooms,
timezone: timezone, timezone: timezone,
users: users users: users
) )
|> stream_configure(:messages,
dom_id: fn
%Message{id: id} -> "messages-#{id}"
:unread_marker -> "messages-unread-marker"
end
)
{:ok, socket} {:ok, socket}
end end
@ -229,10 +244,14 @@ defmodule SlaxWeb.ChatRoomLive do
Chat.get_first_room!() Chat.get_first_room!()
end end
messages = Chat.list_messages_in_room(room) last_read_id = Chat.get_last_read_id(room, socket.assigns.current_user)
messages = room |> Chat.list_messages_in_room() |> maybe_insert_unread_marker(last_read_id)
Chat.subscribe_to_room(room) Chat.subscribe_to_room(room)
Chat.update_last_read_id(room, socket.assigns.current_user)
{:noreply, {:noreply,
socket socket
|> assign( |> assign(
@ -296,6 +315,10 @@ defmodule SlaxWeb.ChatRoomLive do
@impl Phoenix.LiveView @impl Phoenix.LiveView
def handle_info({:new_message, message}, socket) do 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
socket = socket =
socket socket
|> stream_insert(:messages, message) |> stream_insert(:messages, message)
@ -400,4 +423,18 @@ defmodule SlaxWeb.ChatRoomLive do
defp assign_message_form(socket, changeset) do defp assign_message_form(socket, changeset) do
assign(socket, :new_message_form, to_form(changeset)) assign(socket, :new_message_form, to_form(changeset))
end end
defp maybe_insert_unread_marker(messages, nil) do
messages
end
defp maybe_insert_unread_marker(messages, last_read_id) do
{read, unread} = Enum.split_while(messages, &(&1.id <= last_read_id))
if unread == [] do
read
else
read ++ [:unread_marker | unread]
end
end
end end

View File

@ -0,0 +1,9 @@
defmodule Slax.Repo.Migrations.AlterRoomMembershipsAddLastReadId do
use Ecto.Migration
def change do
alter table(:room_memberships) do
add :last_read_id, :integer
end
end
end