add pinned posts
This commit is contained in:
parent
b0e8e521fb
commit
380e9fba21
8 changed files with 242 additions and 7 deletions
|
@ -31,6 +31,7 @@ defmodule Pleroma.User.Info do
|
||||||
field(:hub, :string, default: nil)
|
field(:hub, :string, default: nil)
|
||||||
field(:salmon, :string, default: nil)
|
field(:salmon, :string, default: nil)
|
||||||
field(:hide_network, :boolean, default: false)
|
field(:hide_network, :boolean, default: false)
|
||||||
|
field(:pinned_activities, {:array, :integer}, default: [])
|
||||||
|
|
||||||
# Found in the wild
|
# Found in the wild
|
||||||
# ap_id -> Where is this used?
|
# ap_id -> Where is this used?
|
||||||
|
@ -198,4 +199,26 @@ def admin_api_update(info, params) do
|
||||||
:is_admin
|
:is_admin
|
||||||
])
|
])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def add_pinnned_activity(info, %Pleroma.Activity{id: id}) do
|
||||||
|
if id not in info.pinned_activities do
|
||||||
|
max_pinned_posts = Pleroma.Config.get([:instance, :max_pinned_posts], 0)
|
||||||
|
params = %{pinned_activities: info.pinned_activities ++ [id]}
|
||||||
|
|
||||||
|
info
|
||||||
|
|> cast(params, [:pinned_activities])
|
||||||
|
|> validate_length(:pinned_activities,
|
||||||
|
max: max_pinned_posts,
|
||||||
|
message: "You have already pinned the maximum number of toots"
|
||||||
|
)
|
||||||
|
else
|
||||||
|
change(info)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_pinnned_activity(info, %Pleroma.Activity{id: id}) do
|
||||||
|
params = %{pinned_activities: List.delete(info.pinned_activities, id)}
|
||||||
|
|
||||||
|
cast(info, params, [:pinned_activities])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -394,6 +394,7 @@ def fetch_user_activities(user, reading_user, params \\ %{}) do
|
||||||
|> Map.put("type", ["Create", "Announce"])
|
|> Map.put("type", ["Create", "Announce"])
|
||||||
|> Map.put("actor_id", user.ap_id)
|
|> Map.put("actor_id", user.ap_id)
|
||||||
|> Map.put("whole_db", true)
|
|> Map.put("whole_db", true)
|
||||||
|
|> Map.put("pinned_activity_ids", user.info.pinned_activities)
|
||||||
|
|
||||||
recipients =
|
recipients =
|
||||||
if reading_user do
|
if reading_user do
|
||||||
|
@ -552,6 +553,12 @@ defp restrict_unlisted(query) do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp restrict_pinned(query, %{"pinned" => "true", "pinned_activity_ids" => ids}) do
|
||||||
|
from(activity in query, where: activity.id in ^ids)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp restrict_pinned(query, _), do: query
|
||||||
|
|
||||||
def fetch_activities_query(recipients, opts \\ %{}) do
|
def fetch_activities_query(recipients, opts \\ %{}) do
|
||||||
base_query =
|
base_query =
|
||||||
from(
|
from(
|
||||||
|
@ -576,6 +583,7 @@ def fetch_activities_query(recipients, opts \\ %{}) do
|
||||||
|> restrict_visibility(opts)
|
|> restrict_visibility(opts)
|
||||||
|> restrict_replies(opts)
|
|> restrict_replies(opts)
|
||||||
|> restrict_reblogs(opts)
|
|> restrict_reblogs(opts)
|
||||||
|
|> restrict_pinned(opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_activities(recipients, opts \\ %{}) do
|
def fetch_activities(recipients, opts \\ %{}) do
|
||||||
|
|
|
@ -164,4 +164,38 @@ def update(user) do
|
||||||
object: Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user})
|
object: Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user})
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def pin(id_or_ap_id, user) do
|
||||||
|
with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
|
||||||
|
%{valid?: true} = info_changeset <-
|
||||||
|
Pleroma.User.Info.add_pinnned_activity(user.info, activity),
|
||||||
|
changeset <-
|
||||||
|
Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_changeset),
|
||||||
|
{:ok, _user} <- User.update_and_set_cache(changeset) do
|
||||||
|
{:ok, activity}
|
||||||
|
else
|
||||||
|
%{errors: [pinned_activities: {err, _}]} ->
|
||||||
|
{:error, err}
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
{:error, "Could not pin"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def unpin(id_or_ap_id, user) do
|
||||||
|
with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
|
||||||
|
%{valid?: true} = info_changeset <-
|
||||||
|
Pleroma.User.Info.remove_pinnned_activity(user.info, activity),
|
||||||
|
changeset <-
|
||||||
|
Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_changeset),
|
||||||
|
{:ok, _user} <- User.update_and_set_cache(changeset) do
|
||||||
|
{:ok, activity}
|
||||||
|
else
|
||||||
|
%{errors: [pinned_activities: {err, _}]} ->
|
||||||
|
{:error, err}
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
{:error, "Could not unpin"}
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -256,13 +256,7 @@ def public_timeline(%{assigns: %{user: user}} = conn, params) do
|
||||||
|
|
||||||
def user_statuses(%{assigns: %{user: reading_user}} = conn, params) do
|
def user_statuses(%{assigns: %{user: reading_user}} = conn, params) do
|
||||||
with %User{} = user <- Repo.get(User, params["id"]) do
|
with %User{} = user <- Repo.get(User, params["id"]) do
|
||||||
# Since Pleroma has no "pinned" posts feature, we'll just set an empty list here
|
activities = ActivityPub.fetch_user_activities(user, reading_user, params)
|
||||||
activities =
|
|
||||||
if params["pinned"] == "true" do
|
|
||||||
[]
|
|
||||||
else
|
|
||||||
ActivityPub.fetch_user_activities(user, reading_user, params)
|
|
||||||
end
|
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> add_link_headers(:user_statuses, activities, params["id"])
|
|> add_link_headers(:user_statuses, activities, params["id"])
|
||||||
|
@ -409,6 +403,27 @@ def unfav_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def pin_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||||
|
with {:ok, activity} <- CommonAPI.pin(ap_id_or_id, user) do
|
||||||
|
conn
|
||||||
|
|> put_view(StatusView)
|
||||||
|
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
||||||
|
else
|
||||||
|
{:error, reason} ->
|
||||||
|
conn
|
||||||
|
|> put_resp_content_type("application/json")
|
||||||
|
|> send_resp(:bad_request, Jason.encode!(%{"error" => reason}))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def unpin_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||||
|
with {:ok, activity} <- CommonAPI.unpin(ap_id_or_id, user) do
|
||||||
|
conn
|
||||||
|
|> put_view(StatusView)
|
||||||
|
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def notifications(%{assigns: %{user: user}} = conn, params) do
|
def notifications(%{assigns: %{user: user}} = conn, params) do
|
||||||
notifications = Notification.for_user(user, params)
|
notifications = Notification.for_user(user, params)
|
||||||
|
|
||||||
|
|
|
@ -188,6 +188,8 @@ defmodule Pleroma.Web.Router do
|
||||||
post("/statuses/:id/unreblog", MastodonAPIController, :unreblog_status)
|
post("/statuses/:id/unreblog", MastodonAPIController, :unreblog_status)
|
||||||
post("/statuses/:id/favourite", MastodonAPIController, :fav_status)
|
post("/statuses/:id/favourite", MastodonAPIController, :fav_status)
|
||||||
post("/statuses/:id/unfavourite", MastodonAPIController, :unfav_status)
|
post("/statuses/:id/unfavourite", MastodonAPIController, :unfav_status)
|
||||||
|
post("/statuses/:id/pin", MastodonAPIController, :pin_status)
|
||||||
|
post("/statuses/:id/unpin", MastodonAPIController, :unpin_status)
|
||||||
|
|
||||||
post("/notifications/clear", MastodonAPIController, :clear_notifications)
|
post("/notifications/clear", MastodonAPIController, :clear_notifications)
|
||||||
post("/notifications/dismiss", MastodonAPIController, :dismiss_notification)
|
post("/notifications/dismiss", MastodonAPIController, :dismiss_notification)
|
||||||
|
|
|
@ -601,6 +601,28 @@ test "it can fetch peertube videos" do
|
||||||
assert object
|
assert object
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "returned pinned posts" do
|
||||||
|
Pleroma.Config.put([:instance, :max_pinned_posts], 3)
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity_one} = CommonAPI.post(user, %{"status" => "HI!!!"})
|
||||||
|
{:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
|
||||||
|
{:ok, activity_three} = CommonAPI.post(user, %{"status" => "HI!!!"})
|
||||||
|
|
||||||
|
CommonAPI.pin(activity_one.id, user)
|
||||||
|
|
||||||
|
user = User.get_by_ap_id(user.ap_id)
|
||||||
|
CommonAPI.pin(activity_two.id, user)
|
||||||
|
|
||||||
|
user = User.get_by_ap_id(user.ap_id)
|
||||||
|
CommonAPI.pin(activity_three.id, user)
|
||||||
|
|
||||||
|
user = User.get_by_ap_id(user.ap_id)
|
||||||
|
activities = ActivityPub.fetch_user_activities(user, nil, %{"pinned" => "true"})
|
||||||
|
|
||||||
|
assert 3 = length(activities)
|
||||||
|
end
|
||||||
|
|
||||||
def data_uri do
|
def data_uri do
|
||||||
File.read!("test/fixtures/avatar_data_uri")
|
File.read!("test/fixtures/avatar_data_uri")
|
||||||
end
|
end
|
||||||
|
|
|
@ -96,4 +96,40 @@ test "favoriting a status twice returns an error" do
|
||||||
{:error, _} = CommonAPI.favorite(activity.id, user)
|
{:error, _} = CommonAPI.favorite(activity.id, user)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "pinned posts" do
|
||||||
|
test "pin post" do
|
||||||
|
Pleroma.Config.put([:instance, :max_pinned_posts], 1)
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
|
||||||
|
|
||||||
|
assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "max pinned posts" do
|
||||||
|
Pleroma.Config.put([:instance, :max_pinned_posts], 1)
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity_one} = CommonAPI.post(user, %{"status" => "HI!!!"})
|
||||||
|
{:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
|
||||||
|
|
||||||
|
assert {:ok, ^activity_one} = CommonAPI.pin(activity_one.id, user)
|
||||||
|
|
||||||
|
user = User.get_by_ap_id(user.ap_id)
|
||||||
|
|
||||||
|
assert {:error, "You have already pinned the maximum number of toots"} =
|
||||||
|
CommonAPI.pin(activity_two.id, user)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "unpin post" do
|
||||||
|
Pleroma.Config.put([:instance, :max_pinned_posts], 1)
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
|
||||||
|
{:ok, activity} = CommonAPI.pin(activity.id, user)
|
||||||
|
|
||||||
|
assert {:ok, ^activity} = CommonAPI.unpin(activity.id, user)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1453,4 +1453,99 @@ test "put settings", %{conn: conn} do
|
||||||
user = User.get_cached_by_ap_id(user.ap_id)
|
user = User.get_cached_by_ap_id(user.ap_id)
|
||||||
assert user.info.settings == %{"programming" => "socks"}
|
assert user.info.settings == %{"programming" => "socks"}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "pinned posts" do
|
||||||
|
test "returns pinned posts", %{conn: conn} do
|
||||||
|
Pleroma.Config.put([:instance, :max_pinned_posts], 1)
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
|
||||||
|
{:ok, _} = CommonAPI.pin(activity.id, user)
|
||||||
|
|
||||||
|
result =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
|
||||||
|
|> Map.get(:resp_body)
|
||||||
|
|> Jason.decode!()
|
||||||
|
|
||||||
|
id_str = Integer.to_string(activity.id)
|
||||||
|
|
||||||
|
assert [%{"id" => ^id_str}] = result
|
||||||
|
end
|
||||||
|
|
||||||
|
test "pin post", %{conn: conn} do
|
||||||
|
Pleroma.Config.put([:instance, :max_pinned_posts], 1)
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
|
||||||
|
id_str = Integer.to_string(activity.id)
|
||||||
|
|
||||||
|
assert %{"id" => ^id_str} =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> post("/api/v1/statuses/#{activity.id}/pin")
|
||||||
|
|> Map.get(:resp_body)
|
||||||
|
|> Jason.decode!()
|
||||||
|
|
||||||
|
assert [%{"id" => ^id_str}] =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
|
||||||
|
|> Map.get(:resp_body)
|
||||||
|
|> Jason.decode!()
|
||||||
|
end
|
||||||
|
|
||||||
|
test "unpin post", %{conn: conn} do
|
||||||
|
Pleroma.Config.put([:instance, :max_pinned_posts], 1)
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
|
||||||
|
{:ok, _} = CommonAPI.pin(activity.id, user)
|
||||||
|
|
||||||
|
id_str = Integer.to_string(activity.id)
|
||||||
|
user = User.get_by_ap_id(user.ap_id)
|
||||||
|
|
||||||
|
assert %{"id" => ^id_str} =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> post("/api/v1/statuses/#{activity.id}/unpin")
|
||||||
|
|> Map.get(:resp_body)
|
||||||
|
|> Jason.decode!()
|
||||||
|
|
||||||
|
assert [] =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
|
||||||
|
|> Map.get(:resp_body)
|
||||||
|
|> Jason.decode!()
|
||||||
|
end
|
||||||
|
|
||||||
|
test "max pinned posts", %{conn: conn} do
|
||||||
|
Pleroma.Config.put([:instance, :max_pinned_posts], 1)
|
||||||
|
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity_one} = CommonAPI.post(user, %{"status" => "HI!!!"})
|
||||||
|
{:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
|
||||||
|
|
||||||
|
id_str_one = Integer.to_string(activity_one.id)
|
||||||
|
|
||||||
|
assert %{"id" => ^id_str_one} =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> post("/api/v1/statuses/#{id_str_one}/pin")
|
||||||
|
|> Map.get(:resp_body)
|
||||||
|
|> Jason.decode!()
|
||||||
|
|
||||||
|
user = User.get_by_ap_id(user.ap_id)
|
||||||
|
|
||||||
|
assert %{"error" => "You have already pinned the maximum number of toots"} =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> post("/api/v1/statuses/#{activity_two.id}/pin")
|
||||||
|
|> Map.get(:resp_body)
|
||||||
|
|> Jason.decode!()
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue