From 17a434aa36cf6f73f35f4804f1147388045770a8 Mon Sep 17 00:00:00 2001 From: Liam Date: Sun, 14 Jul 2024 18:56:23 -0400 Subject: [PATCH 1/2] Preliminary cleanup for conversations module --- lib/philomena/conversations.ex | 323 ++++++++---------- lib/philomena/conversations/conversation.ex | 21 +- lib/philomena/conversations/message.ex | 1 + .../message/approve_controller.ex | 2 +- .../conversation/message_controller.ex | 6 +- .../controllers/conversation_controller.ex | 8 +- 6 files changed, 156 insertions(+), 205 deletions(-) diff --git a/lib/philomena/conversations.ex b/lib/philomena/conversations.ex index 82a2e495..1de8fe1c 100644 --- a/lib/philomena/conversations.ex +++ b/lib/philomena/conversations.ex @@ -6,24 +6,9 @@ defmodule Philomena.Conversations do import Ecto.Query, warn: false alias Ecto.Multi alias Philomena.Repo - alias Philomena.Reports alias Philomena.Conversations.Conversation - - @doc """ - Gets a single conversation. - - Raises `Ecto.NoResultsError` if the Conversation does not exist. - - ## Examples - - iex> get_conversation!(123) - %Conversation{} - - iex> get_conversation!(456) - ** (Ecto.NoResultsError) - - """ - def get_conversation!(id), do: Repo.get!(Conversation, id) + alias Philomena.Conversations.Message + alias Philomena.Reports @doc """ Creates a conversation. @@ -41,40 +26,14 @@ defmodule Philomena.Conversations do %Conversation{} |> Conversation.creation_changeset(from, attrs) |> Repo.insert() - end + |> case do + {:ok, conversation} -> + report_non_approved_message(hd(conversation.messages)) + {:ok, conversation} - @doc """ - Updates a conversation. - - ## Examples - - iex> update_conversation(conversation, %{field: new_value}) - {:ok, %Conversation{}} - - iex> update_conversation(conversation, %{field: bad_value}) - {:error, %Ecto.Changeset{}} - - """ - def update_conversation(%Conversation{} = conversation, attrs) do - conversation - |> Conversation.changeset(attrs) - |> Repo.update() - end - - @doc """ - Deletes a Conversation. - - ## Examples - - iex> delete_conversation(conversation) - {:ok, %Conversation{}} - - iex> delete_conversation(conversation) - {:error, %Ecto.Changeset{}} - - """ - def delete_conversation(%Conversation{} = conversation) do - Repo.delete(conversation) + error -> + error + end end @doc """ @@ -90,6 +49,20 @@ defmodule Philomena.Conversations do Conversation.changeset(conversation, %{}) end + @doc """ + Returns the number of unread conversations for the given user. + + Conversations hidden by the given user are not counted. + + ## Examples + + iex> count_unread_conversations(user1) + 0 + + iex> count_unread_conversations(user2) + 7 + + """ def count_unread_conversations(user) do Conversation |> where( @@ -99,187 +72,171 @@ defmodule Philomena.Conversations do not ((c.to_id == ^user.id and c.to_hidden == true) or (c.from_id == ^user.id and c.from_hidden == true)) ) - |> Repo.aggregate(:count, :id) + |> Repo.aggregate(:count) end - def mark_conversation_read(conversation, user, read \\ true) - - def mark_conversation_read( - %Conversation{to_id: user_id, from_id: user_id} = conversation, - %{id: user_id}, - read - ) do - conversation - |> Conversation.read_changeset(%{to_read: read, from_read: read}) - |> Repo.update() - end - - def mark_conversation_read(%Conversation{to_id: user_id} = conversation, %{id: user_id}, read) do - conversation - |> Conversation.read_changeset(%{to_read: read}) - |> Repo.update() - end - - def mark_conversation_read(%Conversation{from_id: user_id} = conversation, %{id: user_id}, read) do - conversation - |> Conversation.read_changeset(%{from_read: read}) - |> Repo.update() - end - - def mark_conversation_read(_conversation, _user, _read), do: {:ok, nil} - - def mark_conversation_hidden(conversation, user, hidden \\ true) - - def mark_conversation_hidden( - %Conversation{to_id: user_id} = conversation, - %{id: user_id}, - hidden - ) do - conversation - |> Conversation.hidden_changeset(%{to_hidden: hidden}) - |> Repo.update() - end - - def mark_conversation_hidden( - %Conversation{from_id: user_id} = conversation, - %{id: user_id}, - hidden - ) do - conversation - |> Conversation.hidden_changeset(%{from_hidden: hidden}) - |> Repo.update() - end - - def mark_conversation_hidden(_conversation, _user, _read), do: {:ok, nil} - - alias Philomena.Conversations.Message - @doc """ - Gets a single message. - - Raises `Ecto.NoResultsError` if the Message does not exist. + Marks a conversation as read or unread from the perspective of the given user. ## Examples - iex> get_message!(123) - %Message{} + iex> mark_conversation_read(conversation, user, true) + {:ok, %Conversation{}} - iex> get_message!(456) - ** (Ecto.NoResultsError) + iex> mark_conversation_read(conversation, user, false) + {:ok, %Conversation{}} + + iex> mark_conversation_read(conversation, %User{}, true) + {:error, %Ecto.Changeset{}} """ - def get_message!(id), do: Repo.get!(Message, id) + def mark_conversation_read(%Conversation{} = conversation, user, read \\ true) do + changes = + %{} + |> put_conditional(:to_read, read, conversation.to_id == user.id) + |> put_conditional(:from_read, read, conversation.from_id == user.id) + + conversation + |> Conversation.read_changeset(changes) + |> Repo.update() + end @doc """ - Creates a message. + Marks a conversation as hidden or visible from the perspective of the given user. + + Hidden conversations are not shown in the list of conversations for the user, and + are not counted when retrieving the number of unread conversations. ## Examples - iex> create_message(%{field: value}) + iex> mark_conversation_hidden(conversation, user, true) + {:ok, %Conversation{}} + + iex> mark_conversation_hidden(conversation, user, false) + {:ok, %Conversation{}} + + iex> mark_conversation_hidden(conversation, %User{}, true) + {:error, %Ecto.Changeset{}} + + """ + def mark_conversation_hidden(%Conversation{} = conversation, user, hidden \\ true) do + changes = + %{} + |> put_conditional(:to_hidden, hidden, conversation.to_id == user.id) + |> put_conditional(:from_hidden, hidden, conversation.from_id == user.id) + + conversation + |> Conversation.hidden_changeset(changes) + |> Repo.update() + end + + defp put_conditional(map, key, value, condition) do + if condition do + Map.put(map, key, value) + else + map + end + end + + @doc """ + Creates a message within a conversation. + + ## Examples + + iex> create_message(%Conversation{}, %User{}, %{field: value}) {:ok, %Message{}} - iex> create_message(%{field: bad_value}) + iex> create_message(%Conversation{}, %User{}, %{field: bad_value}) {:error, %Ecto.Changeset{}} """ def create_message(conversation, user, attrs \\ %{}) do - message = - Ecto.build_assoc(conversation, :messages) + message_changeset = + conversation + |> Ecto.build_assoc(:messages) |> Message.creation_changeset(attrs, user) - show_as_read = - case message do - %{changes: %{approved: true}} -> false - _ -> true - end - - conversation_query = - Conversation - |> where(id: ^conversation.id) - - now = DateTime.utc_now() + conversation_changeset = + Conversation.new_message_changeset(conversation) Multi.new() - |> Multi.insert(:message, message) - |> Multi.update_all(:conversation, conversation_query, - set: [from_read: show_as_read, to_read: show_as_read, last_message_at: now] - ) - |> Repo.transaction() - end - - def approve_conversation_message(message, user) do - reports_query = Reports.close_report_query({"Conversation", message.conversation_id}, user) - - message_query = - message - |> Message.approve_changeset() - - conversation_query = - Conversation - |> where(id: ^message.conversation_id) - - Multi.new() - |> Multi.update(:message, message_query) - |> Multi.update_all(:conversation, conversation_query, set: [to_read: false]) - |> Multi.update_all(:reports, reports_query, []) + |> Multi.insert(:message, message_changeset) + |> Multi.update(:conversation, conversation_changeset) |> Repo.transaction() |> case do - {:ok, %{reports: {_count, reports}} = result} -> - Reports.reindex_reports(reports) + {:ok, %{message: message}} -> + report_non_approved_message(message) + {:ok, message} - {:ok, result} - - error -> - error + _error -> + {:error, message_changeset} end end - def report_non_approved(id) do - Reports.create_system_report( - {"Conversation", id}, - "Approval", - "PM contains externally-embedded images and has been flagged for review." - ) - end - - def set_as_read(conversation) do - conversation - |> Conversation.to_read_changeset() - |> Repo.update() - end - @doc """ - Updates a message. + Approves a previously-posted message which was not approved at post time. ## Examples - iex> update_message(message, %{field: new_value}) + iex> approve_message(%Message{}, %User{}) {:ok, %Message{}} - iex> update_message(message, %{field: bad_value}) + iex> approve_message(%Message{}, %User{}) {:error, %Ecto.Changeset{}} """ - def update_message(%Message{} = message, attrs) do - message - |> Message.changeset(attrs) - |> Repo.update() + def approve_message(message, approving_user) do + message_changeset = Message.approve_changeset(message) + + conversation_update_query = + from c in Conversation, + where: c.id == ^message.conversation_id, + update: [set: [from_read: false, to_read: false]] + + reports_query = + Reports.close_report_query({"Conversation", message.conversation_id}, approving_user) + + Multi.new() + |> Multi.update(:message, message_changeset) + |> Multi.update_all(:conversation, conversation_update_query, []) + |> Multi.update_all(:reports, reports_query, []) + |> Repo.transaction() + |> case do + {:ok, %{reports: {_count, reports}, message: message}} -> + Reports.reindex_reports(reports) + + message + + _error -> + {:error, message_changeset} + end end @doc """ - Deletes a Message. + Generates a system report for an unapproved message. + + This is called by `create_conversation/2` and `create_message/3`, so it normally does not + need to be called explicitly. ## Examples - iex> delete_message(message) - {:ok, %Message{}} + iex> report_non_approved_message(%Message{approved: false}) + {:ok, %Report{}} - iex> delete_message(message) - {:error, %Ecto.Changeset{}} + iex> report_non_approved_message(%Message{approved: true}) + {:ok, nil} """ - def delete_message(%Message{} = message) do - Repo.delete(message) + def report_non_approved_message(message) do + if message.approved do + {:ok, nil} + else + Reports.create_system_report( + {"Conversation", message.conversation_id}, + "Approval", + "PM contains externally-embedded images and has been flagged for review." + ) + end end @doc """ diff --git a/lib/philomena/conversations/conversation.ex b/lib/philomena/conversations/conversation.ex index 77c5981d..d48aa0b9 100644 --- a/lib/philomena/conversations/conversation.ex +++ b/lib/philomena/conversations/conversation.ex @@ -32,19 +32,14 @@ defmodule Philomena.Conversations.Conversation do |> validate_required([]) end + @doc false def read_changeset(conversation, attrs) do - conversation - |> cast(attrs, [:from_read, :to_read]) - end - - def to_read_changeset(conversation) do - change(conversation) - |> put_change(:to_read, true) + cast(conversation, attrs, [:from_read, :to_read]) end + @doc false def hidden_changeset(conversation, attrs) do - conversation - |> cast(attrs, [:from_hidden, :to_hidden]) + cast(conversation, attrs, [:from_hidden, :to_hidden]) end @doc false @@ -61,6 +56,14 @@ defmodule Philomena.Conversations.Conversation do |> validate_length(:messages, is: 1) end + @doc false + def new_message_changeset(conversation) do + conversation + |> change(from_read: false) + |> change(to_read: false) + |> set_last_message() + end + defp set_slug(changeset) do changeset |> change(slug: Ecto.UUID.generate()) diff --git a/lib/philomena/conversations/message.ex b/lib/philomena/conversations/message.ex index a9e6fefd..4dced3af 100644 --- a/lib/philomena/conversations/message.ex +++ b/lib/philomena/conversations/message.ex @@ -33,6 +33,7 @@ defmodule Philomena.Conversations.Message do |> Approval.maybe_put_approval(user) end + @doc false def approve_changeset(message) do change(message, approved: true) end diff --git a/lib/philomena_web/controllers/conversation/message/approve_controller.ex b/lib/philomena_web/controllers/conversation/message/approve_controller.ex index 1693f432..fde13b1a 100644 --- a/lib/philomena_web/controllers/conversation/message/approve_controller.ex +++ b/lib/philomena_web/controllers/conversation/message/approve_controller.ex @@ -16,7 +16,7 @@ defmodule PhilomenaWeb.Conversation.Message.ApproveController do message = conn.assigns.message {:ok, _message} = - Conversations.approve_conversation_message(message, conn.assigns.current_user) + Conversations.approve_message(message, conn.assigns.current_user) conn |> put_flash(:info, "Conversation message approved.") diff --git a/lib/philomena_web/controllers/conversation/message_controller.ex b/lib/philomena_web/controllers/conversation/message_controller.ex index 8bd44940..2d6d2ba2 100644 --- a/lib/philomena_web/controllers/conversation/message_controller.ex +++ b/lib/philomena_web/controllers/conversation/message_controller.ex @@ -20,11 +20,7 @@ defmodule PhilomenaWeb.Conversation.MessageController do user = conn.assigns.current_user case Conversations.create_message(conversation, user, message_params) do - {:ok, %{message: message}} -> - if not message.approved do - Conversations.report_non_approved(message.conversation_id) - end - + {:ok, _message} -> count = Message |> where(conversation_id: ^conversation.id) diff --git a/lib/philomena_web/controllers/conversation_controller.ex b/lib/philomena_web/controllers/conversation_controller.ex index 12784b42..f893fa1f 100644 --- a/lib/philomena_web/controllers/conversation_controller.ex +++ b/lib/philomena_web/controllers/conversation_controller.ex @@ -108,18 +108,12 @@ defmodule PhilomenaWeb.ConversationController do case Conversations.create_conversation(user, conversation_params) do {:ok, conversation} -> - if not hd(conversation.messages).approved do - Conversations.report_non_approved(conversation.id) - Conversations.set_as_read(conversation) - end - conn |> put_flash(:info, "Conversation successfully created.") |> redirect(to: ~p"/conversations/#{conversation}") {:error, changeset} -> - conn - |> render("new.html", changeset: changeset) + render(conn, "new.html", changeset: changeset) end end From 23332bec28ce958dc80c9c4cabce2e5301b73a03 Mon Sep 17 00:00:00 2001 From: Liam Date: Sun, 14 Jul 2024 19:18:25 -0400 Subject: [PATCH 2/2] Move conversation controller functionality into context --- lib/philomena/conversations.ex | 183 ++++++++++++++---- lib/philomena/conversations/conversation.ex | 31 +-- lib/philomena/users.ex | 16 ++ .../conversation/message_controller.ex | 16 +- .../controllers/conversation_controller.ex | 77 ++------ .../templates/conversation/index.html.slime | 8 +- 6 files changed, 197 insertions(+), 134 deletions(-) diff --git a/lib/philomena/conversations.ex b/lib/philomena/conversations.ex index 1de8fe1c..aacf6b94 100644 --- a/lib/philomena/conversations.ex +++ b/lib/philomena/conversations.ex @@ -9,45 +9,7 @@ defmodule Philomena.Conversations do alias Philomena.Conversations.Conversation alias Philomena.Conversations.Message alias Philomena.Reports - - @doc """ - Creates a conversation. - - ## Examples - - iex> create_conversation(%{field: value}) - {:ok, %Conversation{}} - - iex> create_conversation(%{field: bad_value}) - {:error, %Ecto.Changeset{}} - - """ - def create_conversation(from, attrs \\ %{}) do - %Conversation{} - |> Conversation.creation_changeset(from, attrs) - |> Repo.insert() - |> case do - {:ok, conversation} -> - report_non_approved_message(hd(conversation.messages)) - {:ok, conversation} - - error -> - error - end - end - - @doc """ - Returns an `%Ecto.Changeset{}` for tracking conversation changes. - - ## Examples - - iex> change_conversation(conversation) - %Ecto.Changeset{source: %Conversation{}} - - """ - def change_conversation(%Conversation{} = conversation) do - Conversation.changeset(conversation, %{}) - end + alias Philomena.Users @doc """ Returns the number of unread conversations for the given user. @@ -75,6 +37,96 @@ defmodule Philomena.Conversations do |> Repo.aggregate(:count) end + @doc """ + Returns a `m:Scrivener.Page` of conversations between the partner and the user. + + ## Examples + + iex> list_conversations_with("123", %User{}, page_size: 10) + %Scrivener.Page{} + + """ + def list_conversations_with(partner_id, user, pagination) do + query = + from c in Conversation, + where: + (c.from_id == ^partner_id and c.to_id == ^user.id) or + (c.to_id == ^partner_id and c.from_id == ^user.id) + + list_conversations(query, user, pagination) + end + + @doc """ + Returns a `m:Scrivener.Page` of conversations sent by or received from the user. + + ## Examples + + iex> list_conversations_with("123", %User{}, page_size: 10) + %Scrivener.Page{} + + """ + def list_conversations(queryable \\ Conversation, user, pagination) do + query = + from c in queryable, + as: :conversations, + where: + (c.from_id == ^user.id and not c.from_hidden) or + (c.to_id == ^user.id and not c.to_hidden), + inner_lateral_join: + cnt in subquery( + from m in Message, + where: m.conversation_id == parent_as(:conversations).id, + select: %{count: count()} + ), + on: true, + order_by: [desc: :last_message_at], + preload: [:to, :from], + select: %{c | message_count: cnt.count} + + Repo.paginate(query, pagination) + end + + @doc """ + Creates a conversation. + + ## Examples + + iex> create_conversation(from, to, %{field: value}) + {:ok, %Conversation{}} + + iex> create_conversation(from, to, %{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def create_conversation(from, attrs \\ %{}) do + to = Users.get_user_by_name(attrs["recipient"]) + + %Conversation{} + |> Conversation.creation_changeset(from, to, attrs) + |> Repo.insert() + |> case do + {:ok, conversation} -> + report_non_approved_message(hd(conversation.messages)) + {:ok, conversation} + + error -> + error + end + end + + @doc """ + Returns an `%Ecto.Changeset{}` for tracking conversation changes. + + ## Examples + + iex> change_conversation(conversation) + %Ecto.Changeset{source: %Conversation{}} + + """ + def change_conversation(%Conversation{} = conversation) do + Conversation.changeset(conversation, %{}) + end + @doc """ Marks a conversation as read or unread from the perspective of the given user. @@ -138,6 +190,59 @@ defmodule Philomena.Conversations do end end + @doc """ + Returns the number of messages in the given conversation. + + ## Example + + iex> count_messages(%Conversation{}) + 3 + + """ + def count_messages(conversation) do + Message + |> where(conversation_id: ^conversation.id) + |> Repo.aggregate(:count) + end + + @doc """ + Returns a `m:Scrivener.Page` of 2-tuples of messages and rendered output + within a conversation. + + Messages are ordered by user message preference (`messages_newest_first`). + + When coerced to a list and rendered as Markdown, the result may look like: + + [ + {%Message{body: "hello *world*"}, "hello world"} + ] + + ## Example + + iex> list_messages(%Conversation{}, %User{}, & &1.body, page_size: 10) + %Scrivener.Page{} + + """ + def list_messages(conversation, user, collection_renderer, pagination) do + direction = + if user.messages_newest_first do + :desc + else + :asc + end + + query = + from m in Message, + where: m.conversation_id == ^conversation.id, + order_by: [{^direction, :created_at}], + preload: :from + + messages = Repo.paginate(query, pagination) + rendered = collection_renderer.(messages) + + put_in(messages.entries, Enum.zip(messages.entries, rendered)) + end + @doc """ Creates a message within a conversation. diff --git a/lib/philomena/conversations/conversation.ex b/lib/philomena/conversations/conversation.ex index d48aa0b9..b0122eb2 100644 --- a/lib/philomena/conversations/conversation.ex +++ b/lib/philomena/conversations/conversation.ex @@ -4,7 +4,6 @@ defmodule Philomena.Conversations.Conversation do alias Philomena.Users.User alias Philomena.Conversations.Message - alias Philomena.Repo @derive {Phoenix.Param, key: :slug} @@ -20,6 +19,8 @@ defmodule Philomena.Conversations.Conversation do field :from_hidden, :boolean, default: false field :slug, :string field :last_message_at, :utc_datetime + + field :message_count, :integer, virtual: true field :recipient, :string, virtual: true timestamps(inserted_at: :created_at, type: :utc_datetime) @@ -43,17 +44,17 @@ defmodule Philomena.Conversations.Conversation do end @doc false - def creation_changeset(conversation, from, attrs) do + def creation_changeset(conversation, from, to, attrs) do conversation - |> cast(attrs, [:title, :recipient]) - |> validate_required([:title, :recipient]) - |> validate_length(:title, max: 300, count: :bytes) + |> cast(attrs, [:title]) |> put_assoc(:from, from) - |> put_recipient() - |> set_slug() - |> set_last_message() + |> put_assoc(:to, to) + |> put_change(:slug, Ecto.UUID.generate()) |> cast_assoc(:messages, with: &Message.creation_changeset(&1, &2, from)) + |> set_last_message() |> validate_length(:messages, is: 1) + |> validate_length(:title, max: 300, count: :bytes) + |> validate_required([:title, :from, :to]) end @doc false @@ -64,21 +65,7 @@ defmodule Philomena.Conversations.Conversation do |> set_last_message() end - defp set_slug(changeset) do - changeset - |> change(slug: Ecto.UUID.generate()) - end - defp set_last_message(changeset) do change(changeset, last_message_at: DateTime.utc_now(:second)) end - - defp put_recipient(changeset) do - recipient = changeset |> get_field(:recipient) - user = Repo.get_by(User, name: recipient) - - changeset - |> put_change(:to, user) - |> validate_required(:to) - end end diff --git a/lib/philomena/users.ex b/lib/philomena/users.ex index 013b6173..575552aa 100644 --- a/lib/philomena/users.ex +++ b/lib/philomena/users.ex @@ -55,6 +55,22 @@ defmodule Philomena.Users do Repo.get_by(User, email: email) end + @doc """ + Gets a user by name. + + ## Examples + + iex> get_user_by_name("Administrator") + %User{} + + iex> get_user_by_name("nonexistent") + nil + + """ + def get_user_by_name(name) when is_binary(name) do + Repo.get_by(User, name: name) + end + @doc """ Gets a user by email and password. diff --git a/lib/philomena_web/controllers/conversation/message_controller.ex b/lib/philomena_web/controllers/conversation/message_controller.ex index 2d6d2ba2..f6f7fdd1 100644 --- a/lib/philomena_web/controllers/conversation/message_controller.ex +++ b/lib/philomena_web/controllers/conversation/message_controller.ex @@ -1,10 +1,8 @@ defmodule PhilomenaWeb.Conversation.MessageController do use PhilomenaWeb, :controller - alias Philomena.Conversations.{Conversation, Message} + alias Philomena.Conversations.Conversation alias Philomena.Conversations - alias Philomena.Repo - import Ecto.Query plug PhilomenaWeb.FilterBannedUsersPlug plug PhilomenaWeb.CanaryMapPlug, create: :show @@ -15,20 +13,16 @@ defmodule PhilomenaWeb.Conversation.MessageController do id_field: "slug", persisted: true + @page_size 25 + def create(conn, %{"message" => message_params}) do conversation = conn.assigns.conversation user = conn.assigns.current_user case Conversations.create_message(conversation, user, message_params) do {:ok, _message} -> - count = - Message - |> where(conversation_id: ^conversation.id) - |> Repo.aggregate(:count, :id) - - page = - Float.ceil(count / 25) - |> round() + count = Conversations.count_messages(conversation) + page = div(count + @page_size - 1, @page_size) conn |> put_flash(:info, "Message successfully sent.") diff --git a/lib/philomena_web/controllers/conversation_controller.ex b/lib/philomena_web/controllers/conversation_controller.ex index f893fa1f..0e4abdf0 100644 --- a/lib/philomena_web/controllers/conversation_controller.ex +++ b/lib/philomena_web/controllers/conversation_controller.ex @@ -4,8 +4,6 @@ defmodule PhilomenaWeb.ConversationController do alias PhilomenaWeb.NotificationCountPlug alias Philomena.{Conversations, Conversations.Conversation, Conversations.Message} alias PhilomenaWeb.MarkdownRenderer - alias Philomena.Repo - import Ecto.Query plug PhilomenaWeb.FilterBannedUsersPlug when action in [:new, :create] @@ -19,42 +17,17 @@ defmodule PhilomenaWeb.ConversationController do only: :show, preload: [:to, :from] - def index(conn, %{"with" => partner}) do + def index(conn, params) do user = conn.assigns.current_user - Conversation - |> where( - [c], - (c.from_id == ^user.id and c.to_id == ^partner and not c.from_hidden) or - (c.to_id == ^user.id and c.from_id == ^partner and not c.to_hidden) - ) - |> load_conversations(conn) - end - - def index(conn, _params) do - user = conn.assigns.current_user - - Conversation - |> where( - [c], - (c.from_id == ^user.id and not c.from_hidden) or (c.to_id == ^user.id and not c.to_hidden) - ) - |> load_conversations(conn) - end - - defp load_conversations(queryable, conn) do conversations = - queryable - |> join( - :inner_lateral, - [c], - _ in fragment("SELECT COUNT(*) FROM messages m WHERE m.conversation_id = ?", c.id), - on: true - ) - |> order_by(desc: :last_message_at) - |> preload([:to, :from]) - |> select([c, cnt], {c, cnt.count}) - |> Repo.paginate(conn.assigns.scrivener) + case params do + %{"with" => partner_id} -> + Conversations.list_conversations_with(partner_id, user, conn.assigns.scrivener) + + _ -> + Conversations.list_conversations(user, conn.assigns.scrivener) + end render(conn, "index.html", title: "Conversations", conversations: conversations) end @@ -62,27 +35,17 @@ defmodule PhilomenaWeb.ConversationController do def show(conn, _params) do conversation = conn.assigns.conversation user = conn.assigns.current_user - pref = load_direction(user) messages = - Message - |> where(conversation_id: ^conversation.id) - |> order_by([{^pref, :created_at}]) - |> preload([:from]) - |> Repo.paginate(conn.assigns.scrivener) + Conversations.list_messages( + conversation, + user, + &MarkdownRenderer.render_collection(&1, conn), + conn.assigns.scrivener + ) - rendered = - messages.entries - |> MarkdownRenderer.render_collection(conn) - - messages = %{messages | entries: Enum.zip(messages.entries, rendered)} - - changeset = - %Message{} - |> Conversations.change_message() - - conversation - |> Conversations.mark_conversation_read(user) + changeset = Conversations.change_message(%Message{}) + Conversations.mark_conversation_read(conversation, user) # Update the conversation ticker in the header conn = NotificationCountPlug.call(conn) @@ -96,9 +59,10 @@ defmodule PhilomenaWeb.ConversationController do end def new(conn, params) do - changeset = + conversation = %Conversation{recipient: params["recipient"], messages: [%Message{}]} - |> Conversations.change_conversation() + + changeset = Conversations.change_conversation(conversation) render(conn, "new.html", title: "New Conversation", changeset: changeset) end @@ -116,7 +80,4 @@ defmodule PhilomenaWeb.ConversationController do render(conn, "new.html", changeset: changeset) end end - - defp load_direction(%{messages_newest_first: false}), do: :asc - defp load_direction(_user), do: :desc end diff --git a/lib/philomena_web/templates/conversation/index.html.slime b/lib/philomena_web/templates/conversation/index.html.slime index 611610e0..a7c33f33 100644 --- a/lib/philomena_web/templates/conversation/index.html.slime +++ b/lib/philomena_web/templates/conversation/index.html.slime @@ -20,14 +20,14 @@ h1 My Conversations th.table--communication-list__stats With th.table--communication-list__options Options tbody - = for {c, count} <- @conversations do + = for c <- @conversations do tr class=conversation_class(@conn.assigns.current_user, c) td.table--communication-list__name => link c.title, to: ~p"/conversations/#{c}" .small-text.hide-mobile - => count - = pluralize("message", "messages", count) + => c.message_count + = pluralize("message", "messages", c.message_count) ' ; started = pretty_time(c.created_at) ' , last message @@ -36,7 +36,7 @@ h1 My Conversations td.table--communication-list__stats = render PhilomenaWeb.UserAttributionView, "_user.html", object: %{user: other_party(@current_user, c)}, conn: @conn td.table--communication-list__options - => link "Last message", to: last_message_path(c, count) + => link "Last message", to: last_message_path(c, c.message_count) ' • => link "Hide", to: ~p"/conversations/#{c}/hide", data: [method: "post"], data: [confirm: "Are you really, really sure?"]