Add unfollowing remote users

This commit is contained in:
Shadowfacts 2020-04-26 19:28:22 -04:00
parent e194f0fc07
commit 2636b5eabf
Signed by: shadowfacts
GPG Key ID: 94A5AB95422746E5
5 changed files with 64 additions and 8 deletions

View File

@ -132,6 +132,19 @@ defmodule Clacks.ActivityPub do
}
end
@spec undo_follow(actor :: String.t(), follow_activity :: map()) :: map()
def undo_follow(actor, %{"object" => followee} = follow_activity) do
%{
"@context" => @context,
"type" => "Undo",
"id" => activity_id(Ecto.UUID.generate()),
"actor" => actor,
"object" => follow_activity,
"to" => [followee],
"cc" => []
}
end
@spec object_id(id :: String.t()) :: String.t()
def object_id(id) do
url = Application.get_env(:clacks, ClacksWeb.Endpoint)[:url]

View File

@ -67,7 +67,7 @@ defmodule Clacks.ActivityPub.Federator do
@public in activity["to"] or @public in activity["cc"] ->
shared_inbox_for(actor)
actor.data["followers"] in activity["to"] or actor.data["followers"] in activity["cc"] ->
actor["followers"] in activity["to"] or actor["followers"] in activity["cc"] ->
shared_inbox_for(actor)
true ->

View File

@ -73,7 +73,6 @@ defmodule Clacks.Inbox do
} = activity
) do
followee = Actor.get_by_ap_id(followee_id)
follower = Actor.get_by_ap_id(follower_id)
store_activity(activity)
@ -106,6 +105,8 @@ defmodule Clacks.Inbox do
# as a fallback, just store the activity
def handle(activity) do
Logger.debug("Unhandled activity: #{inspect(activity)}")
case store_activity(activity) do
{:error, changeset} ->
Logger.error("Could not store activity: #{inspect(changeset)}")

View File

@ -54,7 +54,7 @@ defmodule Clacks.Object do
end
@spec fetch(ap_id :: String.t(), synthesize_create :: boolean(), return :: :object | :activity) ::
t() | nil
t() | Activity.t() | nil
def fetch(ap_id, synthesize_create \\ true, return \\ :object) do
case Clacks.ActivityPub.Fetcher.fetch_object(ap_id) do
nil ->

View File

@ -174,6 +174,40 @@ defmodule ClacksWeb.FrontendController do
end
end
def unfollow(conn, %{"id" => id}) do
current_user = conn.assigns[:user] |> Repo.preload(:actor)
case Repo.get(Actor, id) do
nil ->
resp(conn, 404, "Not Found")
followee ->
unless current_user.actor.ap_id in followee.followers do
redirect(conn, to: ClacksWeb.FrontendView.local_actor_link(followee))
else
new_followers = List.delete(followee.followers, current_user.actor.ap_id)
changeset = Actor.changeset(followee, %{followers: new_followers})
{:ok, followee} = Repo.update(changeset)
follow_activity = follow_activity(current_user.actor, followee)
changeset =
Activity.changeset(follow_activity, %{
data: %{follow_activity.data | "state" => "unfollowed"}
})
{:ok, follow_activity} = Repo.update(changeset)
undo_follow = ActivityPub.undo_follow(current_user.actor.ap_id, follow_activity.data)
ActivityPub.Helper.save_and_federate(undo_follow, current_user.actor)
conn
|> put_flash(:info, "Unfollowed")
|> redirect(to: ClacksWeb.FrontendView.local_actor_link(followee))
end
end
end
def search(conn, %{"q" => q}) when is_binary(q) do
current_user = conn.assigns[:user]
@ -258,23 +292,31 @@ defmodule ClacksWeb.FrontendController do
ActivityPub.note(current_user.actor.ap_id, content)
end
@spec following_state(follower :: Actor.t(), followee :: Actor.t()) :: boolean()
defp following_state(follower, followee) do
@spec follow_activity(follower :: Actor.t(), followee :: Actor.t()) :: map()
def follow_activity(follower, followee) do
# todo: get latest
query =
Activity
|> where([a], fragment("?->>'type'", a.data) == "Follow")
|> where([a], fragment("?->>'actor'", a.data) == ^follower.ap_id)
|> where([a], fragment("?->>'object'", a.data) == ^followee.ap_id)
|> order_by(desc: :inserted_at)
|> limit(1)
case Repo.one(query) do
nil ->
:not_following
Repo.one(query)
end
@spec following_state(follower :: Actor.t(), followee :: Actor.t()) :: boolean()
defp following_state(follower, followee) do
case follow_activity(follower, followee) do
%Activity{data: %{"state" => "pending"}} ->
:pending
%Activity{data: %{"state" => "accepted"}} ->
:following
_ ->
:not_following
end
end
end