From 4733aa23abdacbdef0a3fefc1acc9445577a2078 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Mon, 18 May 2020 22:27:08 -0400 Subject: [PATCH] Refacotr posting into separate module This will make it easier to keep in sync creating posts from multiple places (e.g. MastoAPI). --- lib/clacks/activitypub.ex | 1 + lib/clacks/user_actions_helper.ex | 60 +++++++++++++++++++ .../controllers/frontend_controller.ex | 47 ++++----------- 3 files changed, 72 insertions(+), 36 deletions(-) create mode 100644 lib/clacks/user_actions_helper.ex diff --git a/lib/clacks/activitypub.ex b/lib/clacks/activitypub.ex index 54b6f31..9e7cd56 100644 --- a/lib/clacks/activitypub.ex +++ b/lib/clacks/activitypub.ex @@ -89,6 +89,7 @@ defmodule Clacks.ActivityPub do @spec synthesized_create(object :: map()) :: map() def synthesized_create(object) do + # todo: does this need to have an id field? %{ "@context" => @context, "type" => "Create", diff --git a/lib/clacks/user_actions_helper.ex b/lib/clacks/user_actions_helper.ex new file mode 100644 index 0000000..5223824 --- /dev/null +++ b/lib/clacks/user_actions_helper.ex @@ -0,0 +1,60 @@ +defmodule Clacks.UserActionsHelper do + alias Clacks.{User, Repo, Activity, Object, ActivityPub} + + @public "https://www.w3.org/ns/activitystreams#Public" + + @spec post_status( + author :: User.t(), + content :: String.t(), + in_reply_to :: String.t() | Activity.t() | nil + ) :: {:ok, Activity.t()} | {:error, any()} + + def post_status(author, content, in_reply_to_ap_id) when is_binary(in_reply_to_ap_id) do + case Activity.get_by_ap_id(in_reply_to_ap_id) do + nil -> + {:error, "Could find post to reply to with AP ID '#{in_reply_to_ap_id}'"} + + in_reply_to -> + post_status(author, content, in_reply_to) + end + end + + def post_status(author, content, in_reply_to) do + note = note_for_posting(author, content, in_reply_to) + note_changeset = Object.changeset_for_creating(note) + {:ok, _object} = Repo.insert(note_changeset) + + %{"id" => ap_id} = create = ActivityPub.create(note) + + case ActivityPub.Helper.save_and_federate(create, author.actor) do + {:ok, activity} -> + {:ok, activity} + + :error -> + {:error, "Unable to save and federate activity with ID '#{ap_id}'"} + end + end + + defp note_for_posting(author, content, %Activity{ + data: %{"id" => in_reply_to_ap_id, "context" => context, "actor" => in_reply_to_actor} + }) do + to = [in_reply_to_actor, @public] + # todo: followers + cc = [] + + ActivityPub.note( + author.actor.ap_id, + content, + context, + in_reply_to_ap_id, + nil, + DateTime.utc_now(), + to, + cc + ) + end + + defp note_for_posting(author, content, _in_reply_to) do + ActivityPub.note(author.actor.ap_id, content) + end +end diff --git a/lib/clacks_web/controllers/frontend_controller.ex b/lib/clacks_web/controllers/frontend_controller.ex index 1eabc9a..0041a0b 100644 --- a/lib/clacks_web/controllers/frontend_controller.ex +++ b/lib/clacks_web/controllers/frontend_controller.ex @@ -1,6 +1,6 @@ defmodule ClacksWeb.FrontendController do use ClacksWeb, :controller - alias Clacks.{Actor, User, Timeline, Repo, ActivityPub, Activity, Object} + alias Clacks.{Actor, User, Timeline, Repo, ActivityPub, Activity, Object, UserActionsHelper} alias ClacksWeb.Router.Helpers, as: Routes alias ClacksWeb.Endpoint import Ecto.Query @@ -251,47 +251,22 @@ defmodule ClacksWeb.FrontendController do }) end - def post_status(conn, %{"content" => _content} = params) do + def post_status(conn, %{"content" => content} = params) do current_user = conn.assigns[:user] |> Repo.preload(:actor) - note = note_for_posting(current_user, params) - note_changeset = Object.changeset_for_creating(note) - {:ok, _object} = Repo.insert(note_changeset) + UserActionsHelper.post_status(current_user, content, Map.get(params, "in_reply_to")) + |> case do + {:ok, activity} -> + path = Map.get(params, "continue", Routes.frontend_path(Endpoint, :status, activity.id)) + redirect(conn, to: path) - create = ActivityPub.create(note) - {:ok, activity} = ActivityPub.Helper.save_and_federate(create, current_user.actor) - - path = Map.get(params, "continue", Routes.frontend_path(Endpoint, :status, activity.id)) - redirect(conn, to: path) - end - - defp note_for_posting(current_user, %{"content" => content, "in_reply_to" => in_reply_to_ap_id}) do - with %Activity{data: %{"context" => context, "actor" => in_reply_to_actor}} <- - Activity.get_by_ap_id(in_reply_to_ap_id) do - to = [in_reply_to_actor, @public] - # todo: followers - cc = [] - - ActivityPub.note( - current_user.actor.ap_id, - content, - context, - in_reply_to_ap_id, - nil, - DateTime.utc_now(), - to, - cc - ) - else - _ -> - ActivityPub.note(current_user.actor.ap_id, content) + {:error, reason} -> + conn + |> put_flash(:error, "Unable to post status: #{inspect(reason)}") + |> redirect(to: Map.get(params, "continue", Routes.frontend_path(Endpoint, :index))) end end - defp note_for_posting(current_user, %{"content" => content}) do - ActivityPub.note(current_user.actor.ap_id, content) - end - @spec follow_activity(follower :: Actor.t(), followee :: Actor.t()) :: map() def follow_activity(follower, followee) do # todo: get latest