Merge branch 'cancel-follow-request' into 'develop'
Add support for cancellation of a follow request Closes #1522 See merge request pleroma/pleroma!2175
This commit is contained in:
commit
1262357ddb
|
@ -121,6 +121,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- OTP releases: Not being able to configure OAuth expired token cleanup interval
|
- OTP releases: Not being able to configure OAuth expired token cleanup interval
|
||||||
- OTP releases: Not being able to configure HTML sanitization policy
|
- OTP releases: Not being able to configure HTML sanitization policy
|
||||||
- Favorites timeline now ordered by favorite date instead of post date
|
- Favorites timeline now ordered by favorite date instead of post date
|
||||||
|
- Support for cancellation of a follow request
|
||||||
<details>
|
<details>
|
||||||
<summary>API Changes</summary>
|
<summary>API Changes</summary>
|
||||||
|
|
||||||
|
|
|
@ -58,8 +58,8 @@ def follow(%User{} = follower, %User{} = following, state \\ "accept") do
|
||||||
|
|
||||||
def unfollow(%User{} = follower, %User{} = following) do
|
def unfollow(%User{} = follower, %User{} = following) do
|
||||||
case get(follower, following) do
|
case get(follower, following) do
|
||||||
nil -> {:ok, nil}
|
|
||||||
%__MODULE__{} = following_relationship -> Repo.delete(following_relationship)
|
%__MODULE__{} = following_relationship -> Repo.delete(following_relationship)
|
||||||
|
_ -> {:ok, nil}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -647,25 +647,48 @@ def follow(%User{} = follower, %User{} = followed, state \\ "accept") do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def unfollow(%User{ap_id: ap_id}, %User{ap_id: ap_id}) do
|
||||||
|
{:error, "Not subscribed!"}
|
||||||
|
end
|
||||||
|
|
||||||
def unfollow(%User{} = follower, %User{} = followed) do
|
def unfollow(%User{} = follower, %User{} = followed) do
|
||||||
if following?(follower, followed) and follower.ap_id != followed.ap_id do
|
case get_follow_state(follower, followed) do
|
||||||
FollowingRelationship.unfollow(follower, followed)
|
state when state in ["accept", "pending"] ->
|
||||||
|
FollowingRelationship.unfollow(follower, followed)
|
||||||
|
{:ok, followed} = update_follower_count(followed)
|
||||||
|
|
||||||
{:ok, followed} = update_follower_count(followed)
|
{:ok, follower} =
|
||||||
|
follower
|
||||||
|
|> update_following_count()
|
||||||
|
|> set_cache()
|
||||||
|
|
||||||
{:ok, follower} =
|
{:ok, follower, Utils.fetch_latest_follow(follower, followed)}
|
||||||
follower
|
|
||||||
|> update_following_count()
|
|
||||||
|> set_cache()
|
|
||||||
|
|
||||||
{:ok, follower, Utils.fetch_latest_follow(follower, followed)}
|
nil ->
|
||||||
else
|
{:error, "Not subscribed!"}
|
||||||
{:error, "Not subscribed!"}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defdelegate following?(follower, followed), to: FollowingRelationship
|
defdelegate following?(follower, followed), to: FollowingRelationship
|
||||||
|
|
||||||
|
def get_follow_state(%User{} = follower, %User{} = following) do
|
||||||
|
following_relationship = FollowingRelationship.get(follower, following)
|
||||||
|
|
||||||
|
case {following_relationship, following.local} do
|
||||||
|
{nil, false} ->
|
||||||
|
case Utils.fetch_latest_follow(follower, following) do
|
||||||
|
%{data: %{"state" => state}} when state in ["pending", "accept"] -> state
|
||||||
|
_ -> nil
|
||||||
|
end
|
||||||
|
|
||||||
|
{%{state: state}, _} ->
|
||||||
|
state
|
||||||
|
|
||||||
|
{nil, _} ->
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def locked?(%User{} = user) do
|
def locked?(%User{} = user) do
|
||||||
user.locked || false
|
user.locked || false
|
||||||
end
|
end
|
||||||
|
|
|
@ -490,6 +490,15 @@ def fetch_latest_follow(%User{ap_id: follower_id}, %User{ap_id: followed_id}) do
|
||||||
|> Repo.one()
|
|> Repo.one()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def fetch_latest_undo(%User{ap_id: ap_id}) do
|
||||||
|
"Undo"
|
||||||
|
|> Activity.Queries.by_type()
|
||||||
|
|> where(actor: ^ap_id)
|
||||||
|
|> order_by([activity], fragment("? desc nulls last", activity.id))
|
||||||
|
|> limit(1)
|
||||||
|
|> Repo.one()
|
||||||
|
end
|
||||||
|
|
||||||
def get_latest_reaction(internal_activity_id, %{ap_id: ap_id}, emoji) do
|
def get_latest_reaction(internal_activity_id, %{ap_id: ap_id}, emoji) do
|
||||||
%{data: %{"object" => object_ap_id}} = Activity.get_by_id(internal_activity_id)
|
%{data: %{"object" => object_ap_id}} = Activity.get_by_id(internal_activity_id)
|
||||||
|
|
||||||
|
|
|
@ -1174,6 +1174,23 @@ test "creates an undo activity for the last follow" do
|
||||||
assert embedded_object["object"] == followed.ap_id
|
assert embedded_object["object"] == followed.ap_id
|
||||||
assert embedded_object["id"] == follow_activity.data["id"]
|
assert embedded_object["id"] == follow_activity.data["id"]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "creates an undo activity for a pending follow request" do
|
||||||
|
follower = insert(:user)
|
||||||
|
followed = insert(:user, %{locked: true})
|
||||||
|
|
||||||
|
{:ok, follow_activity} = ActivityPub.follow(follower, followed)
|
||||||
|
{:ok, activity} = ActivityPub.unfollow(follower, followed)
|
||||||
|
|
||||||
|
assert activity.data["type"] == "Undo"
|
||||||
|
assert activity.data["actor"] == follower.ap_id
|
||||||
|
|
||||||
|
embedded_object = activity.data["object"]
|
||||||
|
assert is_map(embedded_object)
|
||||||
|
assert embedded_object["type"] == "Follow"
|
||||||
|
assert embedded_object["object"] == followed.ap_id
|
||||||
|
assert embedded_object["id"] == follow_activity.data["id"]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "blocking / unblocking" do
|
describe "blocking / unblocking" do
|
||||||
|
|
|
@ -551,6 +551,50 @@ test "also unsubscribes a user" do
|
||||||
|
|
||||||
refute User.subscribed_to?(follower, followed)
|
refute User.subscribed_to?(follower, followed)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "cancels a pending follow for a local user" do
|
||||||
|
follower = insert(:user)
|
||||||
|
followed = insert(:user, locked: true)
|
||||||
|
|
||||||
|
assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =
|
||||||
|
CommonAPI.follow(follower, followed)
|
||||||
|
|
||||||
|
assert User.get_follow_state(follower, followed) == "pending"
|
||||||
|
assert {:ok, follower} = CommonAPI.unfollow(follower, followed)
|
||||||
|
assert User.get_follow_state(follower, followed) == nil
|
||||||
|
|
||||||
|
assert %{id: ^activity_id, data: %{"state" => "cancelled"}} =
|
||||||
|
Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(follower, followed)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
data: %{
|
||||||
|
"type" => "Undo",
|
||||||
|
"object" => %{"type" => "Follow", "state" => "cancelled"}
|
||||||
|
}
|
||||||
|
} = Pleroma.Web.ActivityPub.Utils.fetch_latest_undo(follower)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "cancels a pending follow for a remote user" do
|
||||||
|
follower = insert(:user)
|
||||||
|
followed = insert(:user, locked: true, local: false, ap_enabled: true)
|
||||||
|
|
||||||
|
assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =
|
||||||
|
CommonAPI.follow(follower, followed)
|
||||||
|
|
||||||
|
assert User.get_follow_state(follower, followed) == "pending"
|
||||||
|
assert {:ok, follower} = CommonAPI.unfollow(follower, followed)
|
||||||
|
assert User.get_follow_state(follower, followed) == nil
|
||||||
|
|
||||||
|
assert %{id: ^activity_id, data: %{"state" => "cancelled"}} =
|
||||||
|
Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(follower, followed)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
data: %{
|
||||||
|
"type" => "Undo",
|
||||||
|
"object" => %{"type" => "Follow", "state" => "cancelled"}
|
||||||
|
}
|
||||||
|
} = Pleroma.Web.ActivityPub.Utils.fetch_latest_undo(follower)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "accept_follow_request/2" do
|
describe "accept_follow_request/2" do
|
||||||
|
|
|
@ -457,6 +457,16 @@ test "following / unfollowing a user", %{conn: conn} do
|
||||||
assert id == to_string(other_user.id)
|
assert id == to_string(other_user.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "cancelling follow request", %{conn: conn} do
|
||||||
|
%{id: other_user_id} = insert(:user, %{locked: true})
|
||||||
|
|
||||||
|
assert %{"id" => ^other_user_id, "following" => false, "requested" => true} =
|
||||||
|
conn |> post("/api/v1/accounts/#{other_user_id}/follow") |> json_response(:ok)
|
||||||
|
|
||||||
|
assert %{"id" => ^other_user_id, "following" => false, "requested" => false} =
|
||||||
|
conn |> post("/api/v1/accounts/#{other_user_id}/unfollow") |> json_response(:ok)
|
||||||
|
end
|
||||||
|
|
||||||
test "following without reblogs" do
|
test "following without reblogs" do
|
||||||
%{conn: conn} = oauth_access(["follow", "read:statuses"])
|
%{conn: conn} = oauth_access(["follow", "read:statuses"])
|
||||||
followed = insert(:user)
|
followed = insert(:user)
|
||||||
|
|
Loading…
Reference in a new issue