slax/lib/slax/chat.ex

173 lines
4.1 KiB
Elixir

defmodule Slax.Chat do
@moduledoc """
Context to handle with chat details.
"""
import Ecto.Changeset
import Ecto.Query
alias Slax.Accounts.User
alias Slax.Chat.Message
alias Slax.Chat.Room
alias Slax.Chat.RoomMembership
alias Slax.Repo
@pubsub Slax.PubSub
def create_room(attrs) do
%Room{}
|> change_room(attrs)
|> Repo.insert()
end
def update_room(%Room{} = room, attrs) do
room
|> Room.changeset(attrs)
|> Repo.update()
end
def join_room!(%Room{} = room, %User{} = user) do
Repo.insert!(%RoomMembership{room: room, user: user})
end
def joined?(%Room{} = room, %User{} = user) do
Repo.exists?(
from rm in RoomMembership, where: rm.room_id == ^room.id and rm.user_id == ^user.id
)
end
def toggle_room_membership(%Room{} = room, %User{} = user) do
case get_membership(room, user) do
%RoomMembership{} = membership ->
Repo.delete(membership)
{room, false}
nil ->
join_room!(room, user)
{room, true}
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
Room.changeset(room, attrs)
end
def get_first_room! do
query = from(r in Room, limit: 1, order_by: [asc: :name])
Repo.one!(query)
end
def get_room!(id) do
Repo.get!(Room, id)
end
def list_rooms do
query = from(r in Room, order_by: [asc: :name])
Repo.all(query)
end
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
query =
from(r in Room,
left_join: m in RoomMembership,
on: r.id == m.room_id and m.user_id == ^user.id,
select: {r, not is_nil(m.id)},
order_by: [asc: :name]
)
Repo.all(query)
end
def list_messages_in_room(%Room{id: room_id}) do
query =
from(m in Message,
inner_join: u in assoc(m, :user),
preload: [user: u],
where: m.room_id == ^room_id,
order_by: [asc: m.inserted_at, asc: m.id]
)
Repo.all(query)
end
def create_message(room, user, attrs) do
with {:ok, message} <-
%Message{room: room, user: user}
|> change_message(attrs)
|> Repo.insert() do
Phoenix.PubSub.broadcast!(@pubsub, topic(room), {:new_message, message})
{:ok, message}
end
end
def change_message(message, attrs \\ %{}) do
Message.changeset(message, attrs)
end
def delete_message_by_id(message_id, %User{id: user_id}) do
with {:ok, message} <-
Message
|> Repo.get_by(id: message_id, user_id: user_id)
|> Repo.delete() do
Phoenix.PubSub.broadcast!(@pubsub, topic(message.room_id), {:deleted_message, message})
{:ok, message}
end
end
def subscribe_to_room(room) do
Phoenix.PubSub.subscribe(@pubsub, topic(room))
end
def unsubscribe_from_room(room) do
Phoenix.PubSub.unsubscribe(@pubsub, topic(room))
end
defp topic(%Room{id: room_id}), do: "chat_room:#{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