diff --git a/lib/algora/admin/admin.ex b/lib/algora/admin/admin.ex index a25b0bea3..2409ecb90 100644 --- a/lib/algora/admin/admin.ex +++ b/lib/algora/admin/admin.ex @@ -330,6 +330,16 @@ defmodule Algora.Admin do |> Algora.Repo.update() end + def admins_last_active do + Algora.Repo.one( + from u in User, + where: u.is_admin == true, + order_by: [desc: u.last_active_at], + select: u.last_active_at, + limit: 1 + ) + end + def setup_test_account(user_handle) do with account_id when is_binary(account_id) <- Algora.config([:stripe, :test_account_id]), {:ok, user} <- Repo.fetch_by(User, handle: user_handle), diff --git a/lib/algora/chat/chat.ex b/lib/algora/chat/chat.ex index 41a6eaff7..bd45843a4 100644 --- a/lib/algora/chat/chat.ex +++ b/lib/algora/chat/chat.ex @@ -8,6 +8,14 @@ defmodule Algora.Chat do alias Algora.Chat.Thread alias Algora.Repo + def broadcast(message) do + Phoenix.PubSub.broadcast(Algora.PubSub, "chat:thread:#{message.thread_id}", message) + end + + def subscribe(thread_id) do + Phoenix.PubSub.subscribe(Algora.PubSub, "chat:thread:#{thread_id}") + end + def create_direct_thread(user_1, user_2) do Repo.transaction(fn -> {:ok, thread} = @@ -30,14 +38,44 @@ defmodule Algora.Chat do end) end + def create_admin_thread(user, admins) do + Repo.transaction(fn -> + {:ok, thread} = + %Thread{} + |> Thread.changeset(%{title: "Chat with Algora founders"}) + |> Repo.insert() + + participants = Enum.uniq_by([user | admins], & &1.id) + # Add participants + for u <- participants do + %Participant{} + |> Participant.changeset(%{ + thread_id: thread.id, + user_id: u.id, + last_read_at: DateTime.utc_now() + }) + |> Repo.insert!() + end + + thread + end) + end + def send_message(thread_id, sender_id, content) do - %Message{} - |> Message.changeset(%{ - thread_id: thread_id, - sender_id: sender_id, - content: content - }) - |> Repo.insert() + case %Message{} + |> Message.changeset(%{ + thread_id: thread_id, + sender_id: sender_id, + content: content + }) + |> Repo.insert() do + {:ok, message} -> + message |> Repo.preload(:sender) |> broadcast() + {:ok, message} + + {:error, changeset} -> + {:error, changeset} + end end def list_messages(thread_id, limit \\ 50) do @@ -48,11 +86,24 @@ defmodule Algora.Chat do |> Repo.all() end - def list_threads(user_id) do + def get_thread(thread_id) do + Repo.get(Thread, thread_id) + end + + # TODO: filter by user_id + def list_threads(_user_id) do + last_message_query = + from m in Message, + select: %{ + thread_id: m.thread_id, + last_message_at: max(m.inserted_at) + }, + group_by: m.thread_id + Thread - |> join(:inner, [t], p in Participant, on: p.thread_id == t.id) - |> where([_t, p], p.user_id == ^user_id) - |> preload(:participants) + |> join(:left, [t], lm in subquery(last_message_query), on: t.id == lm.thread_id) + |> order_by([t, lm], desc: lm.last_message_at) + |> preload(participants: :user) |> Repo.all() end @@ -62,18 +113,20 @@ defmodule Algora.Chat do |> Repo.update_all(set: [last_read_at: DateTime.utc_now()]) end - def get_thread_for_users(user1_id, user2_id) do + def get_thread_for_users(users) do + participants = Enum.uniq_by(users, & &1.id) + Thread |> join(:inner, [t], p in Participant, on: p.thread_id == t.id) - |> where([t, p], p.user_id in [^user1_id, ^user2_id]) + |> where([t, p], p.user_id in ^Enum.map(participants, & &1.id)) |> group_by([t], t.id) - |> having([t, p], count(p.id) == 2) + |> having([t, p], count(p.id) == ^length(participants)) |> limit(1) |> Repo.one() end def get_or_create_thread(contract) do - case get_thread_for_users(contract.client_id, contract.contractor_id) do + case get_thread_for_users([contract.client, contract.contractor]) do nil -> create_direct_thread(contract.client, contract.contractor) thread -> {:ok, thread} end @@ -83,4 +136,13 @@ defmodule Algora.Chat do {:ok, thread} = get_or_create_thread(contract) thread end + + def get_or_create_admin_thread(current_user) do + admins = Repo.all(from u in User, where: u.is_admin == true) + + case get_thread_for_users([current_user] ++ admins) do + nil -> create_admin_thread(current_user, admins) + thread -> {:ok, thread} + end + end end diff --git a/lib/algora_web/components/layouts/user.html.heex b/lib/algora_web/components/layouts/user.html.heex index 3104a7887..2eca6d79d 100644 --- a/lib/algora_web/components/layouts/user.html.heex +++ b/lib/algora_web/components/layouts/user.html.heex @@ -106,14 +106,14 @@ <.separator :if={index != length(@nav) - 1} class="mx-auto my-4 w-2/3" /> <% end %> - <%= if @contacts != [] do %> + <%= if assigns[:threads] && assigns[:threads] != [] do %> <.separator class="mx-auto my-4 w-2/3" />
+ {@thread.participants + |> Enum.map(& &1.user.name) + |> Algora.Util.format_name_list()} +
++ Active {Algora.Util.time_ago(@admins_last_active)} +
+