Fix Pleroma.FollowingRelationship.move_following/2
[akkoma] / lib / pleroma / following_relationship.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.FollowingRelationship do
6 use Ecto.Schema
7
8 import Ecto.Changeset
9 import Ecto.Query
10
11 alias FlakeId.Ecto.CompatType
12 alias Pleroma.Repo
13 alias Pleroma.User
14
15 schema "following_relationships" do
16 field(:state, :string, default: "accept")
17
18 belongs_to(:follower, User, type: CompatType)
19 belongs_to(:following, User, type: CompatType)
20
21 timestamps()
22 end
23
24 def changeset(%__MODULE__{} = following_relationship, attrs) do
25 following_relationship
26 |> cast(attrs, [:state])
27 |> put_assoc(:follower, attrs.follower)
28 |> put_assoc(:following, attrs.following)
29 |> validate_required([:state, :follower, :following])
30 end
31
32 def get(%User{} = follower, %User{} = following) do
33 __MODULE__
34 |> where(follower_id: ^follower.id, following_id: ^following.id)
35 |> Repo.one()
36 end
37
38 def update(follower, following, "reject"), do: unfollow(follower, following)
39
40 def update(%User{} = follower, %User{} = following, state) do
41 case get(follower, following) do
42 nil ->
43 follow(follower, following, state)
44
45 following_relationship ->
46 following_relationship
47 |> cast(%{state: state}, [:state])
48 |> validate_required([:state])
49 |> Repo.update()
50 end
51 end
52
53 def follow(%User{} = follower, %User{} = following, state \\ "accept") do
54 %__MODULE__{}
55 |> changeset(%{follower: follower, following: following, state: state})
56 |> Repo.insert(on_conflict: :nothing)
57 end
58
59 def unfollow(%User{} = follower, %User{} = following) do
60 case get(follower, following) do
61 nil -> {:ok, nil}
62 %__MODULE__{} = following_relationship -> Repo.delete(following_relationship)
63 end
64 end
65
66 def follower_count(%User{} = user) do
67 %{followers: user, deactivated: false}
68 |> User.Query.build()
69 |> Repo.aggregate(:count, :id)
70 end
71
72 def following_count(%User{id: nil}), do: 0
73
74 def following_count(%User{} = user) do
75 %{friends: user, deactivated: false}
76 |> User.Query.build()
77 |> Repo.aggregate(:count, :id)
78 end
79
80 def get_follow_requests(%User{id: id}) do
81 __MODULE__
82 |> join(:inner, [r], f in assoc(r, :follower))
83 |> where([r], r.state == "pending")
84 |> where([r], r.following_id == ^id)
85 |> select([r, f], f)
86 |> Repo.all()
87 end
88
89 def following?(%User{id: follower_id}, %User{id: followed_id}) do
90 __MODULE__
91 |> where(follower_id: ^follower_id, following_id: ^followed_id, state: "accept")
92 |> Repo.exists?()
93 end
94
95 def following(%User{} = user) do
96 following =
97 __MODULE__
98 |> join(:inner, [r], u in User, on: r.following_id == u.id)
99 |> where([r], r.follower_id == ^user.id)
100 |> where([r], r.state == "accept")
101 |> select([r, u], u.follower_address)
102 |> Repo.all()
103
104 if not user.local or user.invisible do
105 following
106 else
107 [user.follower_address | following]
108 end
109 end
110
111 def move_following(origin, target) do
112 __MODULE__
113 |> join(:inner, [r], f in assoc(r, :follower))
114 |> where(following_id: ^origin.id)
115 |> where([r, f], f.allow_following_move == true)
116 |> limit(50)
117 |> preload([:follower])
118 |> Repo.all()
119 |> Enum.map(fn following_relationship ->
120 Repo.delete(following_relationship)
121 Pleroma.Web.CommonAPI.follow(following_relationship.follower, target)
122 end)
123 |> case do
124 [] ->
125 User.update_follower_count(origin)
126 :ok
127
128 _ ->
129 move_following(origin, target)
130 end
131 end
132 end