1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.FollowingRelationship do
11 alias FlakeId.Ecto.CompatType
15 schema "following_relationships" do
16 field(:state, :string, default: "accept")
18 belongs_to(:follower, User, type: CompatType)
19 belongs_to(:following, User, type: CompatType)
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])
32 def get(%User{} = follower, %User{} = following) do
34 |> where(follower_id: ^follower.id, following_id: ^following.id)
38 def update(follower, following, "reject"), do: unfollow(follower, following)
40 def update(%User{} = follower, %User{} = following, state) do
41 case get(follower, following) do
43 follow(follower, following, state)
45 following_relationship ->
46 following_relationship
47 |> cast(%{state: state}, [:state])
48 |> validate_required([:state])
53 def follow(%User{} = follower, %User{} = following, state \\ "accept") do
55 |> changeset(%{follower: follower, following: following, state: state})
56 |> Repo.insert(on_conflict: :nothing)
59 def unfollow(%User{} = follower, %User{} = following) do
60 case get(follower, following) do
62 %__MODULE__{} = following_relationship -> Repo.delete(following_relationship)
66 def follower_count(%User{} = user) do
67 %{followers: user, deactivated: false}
69 |> Repo.aggregate(:count, :id)
72 def following_count(%User{id: nil}), do: 0
74 def following_count(%User{} = user) do
75 %{friends: user, deactivated: false}
77 |> Repo.aggregate(:count, :id)
80 def get_follow_requests(%User{id: id}) do
82 |> join(:inner, [r], f in assoc(r, :follower))
83 |> where([r], r.state == "pending")
84 |> where([r], r.following_id == ^id)
89 def following?(%User{id: follower_id}, %User{id: followed_id}) do
91 |> where(follower_id: ^follower_id, following_id: ^followed_id, state: "accept")
95 def following(%User{} = user) do
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)
104 if not user.local or user.invisible do
107 [user.follower_address | following]
111 def move_following(origin, target) do
113 |> join(:inner, [r], f in assoc(r, :follower))
114 |> where(following_id: ^origin.id)
115 |> where([r, f], f.allow_following_move == true)
117 |> preload([:follower])
119 |> Enum.map(fn following_relationship ->
120 Repo.delete(following_relationship)
121 Pleroma.Web.CommonAPI.follow(following_relationship.follower, target)
125 _ -> move_following(origin, target)