feat: Support unfollowing
This commit is contained in:
parent
dc7c855095
commit
e89cc78274
8 changed files with 97 additions and 9 deletions
|
|
@ -73,7 +73,7 @@
|
||||||
## Protocol Support
|
## Protocol Support
|
||||||
|
|
||||||
- [x] Check that signature header (digest) matches digest of body contents
|
- [x] Check that signature header (digest) matches digest of body contents
|
||||||
- [ ] Check the domain of the public key against the domain of the object being CRUDed
|
- [x] Check the domain of the public key against the domain of the object being CRUDed
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,9 +38,12 @@ defmodule ActivityPub do
|
||||||
|> Req.Request.put_header("accept", "application/json")
|
|> Req.Request.put_header("accept", "application/json")
|
||||||
|
|
||||||
case Req.get(request) do
|
case Req.get(request) do
|
||||||
{:ok, result} ->
|
{:ok, %{status: 200} = result} ->
|
||||||
{:ok, result.body}
|
{:ok, result.body}
|
||||||
|
|
||||||
|
{:ok, %{status: 404}} ->
|
||||||
|
nil
|
||||||
|
|
||||||
error ->
|
error ->
|
||||||
error
|
error
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,9 @@ defmodule ActivityPub.Headers do
|
||||||
{:ok, public_key} ->
|
{:ok, public_key} ->
|
||||||
:public_key.verify(to_verify, :sha256, signature, public_key)
|
:public_key.verify(to_verify, :sha256, signature, public_key)
|
||||||
|
|
||||||
|
nil ->
|
||||||
|
false
|
||||||
|
|
||||||
error ->
|
error ->
|
||||||
error
|
error
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,13 @@ defmodule Postland.Actors do
|
||||||
def actor(actor_id) do
|
def actor(actor_id) do
|
||||||
result =
|
result =
|
||||||
Cachex.fetch(:main_cache, "actor:#{actor_id}", fn _key ->
|
Cachex.fetch(:main_cache, "actor:#{actor_id}", fn _key ->
|
||||||
{:ok, actor} = ActivityPub.fetch_actor(actor_id)
|
case ActivityPub.fetch_actor(actor_id) do
|
||||||
|
{:ok, actor} ->
|
||||||
|
{:commit, actor, expire: :timer.seconds(300)}
|
||||||
|
|
||||||
{:commit, actor, expire: :timer.seconds(300)}
|
nil ->
|
||||||
|
{:commit, nil, expire: :timer.seconds(300)}
|
||||||
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
case result do
|
case result do
|
||||||
|
|
|
||||||
|
|
@ -52,14 +52,30 @@ defmodule Postland.Follows do
|
||||||
def record_and_send_withdrawl(request) do
|
def record_and_send_withdrawl(request) do
|
||||||
Multi.new()
|
Multi.new()
|
||||||
|> Multi.delete(:delete, request)
|
|> Multi.delete(:delete, request)
|
||||||
|> Multi.run(:send_acceptance, fn _data, _repo ->
|
|> Multi.run(:send_withdrawl, fn _data, _repo ->
|
||||||
send_withdrawl(request)
|
send_withdrawl(request)
|
||||||
end)
|
end)
|
||||||
|> Repo.transaction()
|
|> Repo.transaction()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def record_and_unfollow(follow) do
|
||||||
|
Multi.new()
|
||||||
|
|> Multi.delete(:delete, follow)
|
||||||
|
|> Multi.run(:send_delete_follow, fn _data, _repo ->
|
||||||
|
send_delete_follow(follow)
|
||||||
|
end)
|
||||||
|
|> Repo.transaction()
|
||||||
|
end
|
||||||
|
|
||||||
def send_withdrawl(request) do
|
def send_withdrawl(request) do
|
||||||
{:ok, actor} = ActivityPub.fetch_actor(request.followee)
|
request.followee
|
||||||
|
|> ActivityPub.fetch_actor()
|
||||||
|
|> do_send_withdrawl(request)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp do_send_withdrawl(nil, _), do: {:ok, nil}
|
||||||
|
|
||||||
|
defp do_send_withdrawl({:ok, actor}, request) do
|
||||||
inbox = Map.get(actor, "inbox")
|
inbox = Map.get(actor, "inbox")
|
||||||
|
|
||||||
body =
|
body =
|
||||||
|
|
@ -84,7 +100,14 @@ defmodule Postland.Follows do
|
||||||
end
|
end
|
||||||
|
|
||||||
def send_acceptance(request) do
|
def send_acceptance(request) do
|
||||||
{:ok, actor} = ActivityPub.fetch_actor(request.follower)
|
request.follower
|
||||||
|
|> ActivityPub.fetch_actor()
|
||||||
|
|> do_send_acceptance(request)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp do_send_acceptance(nil, _), do: {:ok, nil}
|
||||||
|
|
||||||
|
defp do_send_acceptance({:ok, actor}, request) do
|
||||||
inbox = Map.get(actor, "inbox")
|
inbox = Map.get(actor, "inbox")
|
||||||
|
|
||||||
case get_follow_activity(request.follower) do
|
case get_follow_activity(request.follower) do
|
||||||
|
|
@ -180,6 +203,34 @@ defmodule Postland.Follows do
|
||||||
Req.post(inbox, headers: headers, body: follow_request)
|
Req.post(inbox, headers: headers, body: follow_request)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def send_delete_follow(follow) do
|
||||||
|
encoded_followee = Base.url_encode64(follow.followee, padding: false)
|
||||||
|
encoded_follower = Base.url_encode64(follow.follower, padding: false)
|
||||||
|
|
||||||
|
{:ok, actor} = ActivityPub.fetch_actor(follow.followee)
|
||||||
|
inbox = Map.get(actor, "inbox")
|
||||||
|
|
||||||
|
follow_request =
|
||||||
|
%{
|
||||||
|
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||||
|
"type" => "Delete",
|
||||||
|
"actor" => Postland.my_actor_id(),
|
||||||
|
"object" => url(~p"/follows/#{encoded_followee}/#{encoded_follower}")
|
||||||
|
}
|
||||||
|
|> Jason.encode!()
|
||||||
|
|
||||||
|
headers =
|
||||||
|
Headers.signing_headers(
|
||||||
|
"POST",
|
||||||
|
inbox,
|
||||||
|
follow_request,
|
||||||
|
Postland.my_actor_id(),
|
||||||
|
Accounts.solo_user().private_key
|
||||||
|
)
|
||||||
|
|
||||||
|
Req.post(inbox, headers: headers, body: follow_request)
|
||||||
|
end
|
||||||
|
|
||||||
def get(follower, followee) do
|
def get(follower, followee) do
|
||||||
from(f in Follow, where: f.followee == ^followee, where: f.follower == ^follower)
|
from(f in Follow, where: f.followee == ^followee, where: f.follower == ^follower)
|
||||||
|> Repo.one()
|
|> Repo.one()
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,14 @@ defmodule PostlandWeb.CoreComponents do
|
||||||
>
|
>
|
||||||
Withdraw Request
|
Withdraw Request
|
||||||
</.button>
|
</.button>
|
||||||
|
<.button
|
||||||
|
:if={@status == :following_confirmed}
|
||||||
|
phx-click="unfollow"
|
||||||
|
phx-value-dom-id={@dom_id}
|
||||||
|
phx-value-id={@account["id"]}
|
||||||
|
>
|
||||||
|
Unfollow
|
||||||
|
</.button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -177,7 +185,7 @@ defmodule PostlandWeb.CoreComponents do
|
||||||
end
|
end
|
||||||
|
|
||||||
def post_card(assigns) do
|
def post_card(assigns) do
|
||||||
author = Postland.Timeline.attribution(assigns.post) || %{}
|
author = Postland.Timeline.attribution(assigns.post)
|
||||||
|
|
||||||
%{host: host} =
|
%{host: host} =
|
||||||
case author do
|
case author do
|
||||||
|
|
@ -201,7 +209,7 @@ defmodule PostlandWeb.CoreComponents do
|
||||||
|> Map.put(:cw, cw)
|
|> Map.put(:cw, cw)
|
||||||
|
|
||||||
~H"""
|
~H"""
|
||||||
<div class="flex items-start space-x-4 w-full">
|
<div :if={@author} class="flex items-start space-x-4 w-full">
|
||||||
<div class="flex-shrink-0">
|
<div class="flex-shrink-0">
|
||||||
<img
|
<img
|
||||||
class="inline-block h-16 w-16 rounded-full drop-shadow-lg"
|
class="inline-block h-16 w-16 rounded-full drop-shadow-lg"
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ defmodule PostlandWeb.FollowersLive do
|
||||||
account: Actors.actor(follow.follower)
|
account: Actors.actor(follow.follower)
|
||||||
}
|
}
|
||||||
end)
|
end)
|
||||||
|
|> Enum.reject(&is_nil(&1.account))
|
||||||
|
|
||||||
account_count = Enum.count(followers)
|
account_count = Enum.count(followers)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ defmodule PostlandWeb.FollowingLive do
|
||||||
account: Actors.actor(follow.followee)
|
account: Actors.actor(follow.followee)
|
||||||
}
|
}
|
||||||
end)
|
end)
|
||||||
|
|> Enum.reject(&is_nil(&1.account))
|
||||||
|
|
||||||
account_count = Enum.count(follows)
|
account_count = Enum.count(follows)
|
||||||
|
|
||||||
|
|
@ -60,4 +61,21 @@ defmodule PostlandWeb.FollowingLive do
|
||||||
|
|
||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def handle_event("unfollow", %{"id" => id, "dom-id" => dom_id}, socket) do
|
||||||
|
request = Follows.get(Postland.my_actor_id(), id)
|
||||||
|
|
||||||
|
socket =
|
||||||
|
case Follows.record_and_unfollow(request) do
|
||||||
|
{:ok, _} ->
|
||||||
|
socket
|
||||||
|
|> stream_delete_by_dom_id(:accounts, dom_id)
|
||||||
|
|> assign(:count, socket.assigns.count - 1)
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
put_flash(socket, :error, "An unexpected error occurred.")
|
||||||
|
end
|
||||||
|
|
||||||
|
{:noreply, socket}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue