173 lines
4.1 KiB
Elixir
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
|