bookmarks in separate table
This commit is contained in:
parent
030a7876b4
commit
73d01857e3
7 changed files with 135 additions and 52 deletions
53
lib/pleroma/bookmark.ex
Normal file
53
lib/pleroma/bookmark.ex
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
defmodule Pleroma.Bookmark do
|
||||||
|
use Ecto.Schema
|
||||||
|
|
||||||
|
import Ecto.Changeset
|
||||||
|
import Ecto.Query
|
||||||
|
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Bookmark
|
||||||
|
alias Pleroma.FlakeId
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
|
@type t :: %__MODULE__{}
|
||||||
|
|
||||||
|
schema "bookmarks" do
|
||||||
|
belongs_to(:user, User, type: FlakeId)
|
||||||
|
belongs_to(:activity, Activity, type: FlakeId)
|
||||||
|
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec create(FlakeId.t(), FlakeId.t()) :: {:ok, Bookmark.t()} | {:error, Changeset.t()}
|
||||||
|
def create(user_id, activity_id) do
|
||||||
|
attrs = %{
|
||||||
|
user_id: user_id,
|
||||||
|
activity_id: activity_id
|
||||||
|
}
|
||||||
|
|
||||||
|
%Bookmark{}
|
||||||
|
|> cast(attrs, [:user_id, :activity_id])
|
||||||
|
|> validate_required([:user_id, :activity_id])
|
||||||
|
|> unique_constraint(:activity_id, name: :bookmarks_user_id_activity_id_index)
|
||||||
|
|> Repo.insert()
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec for_user_query(FlakeId.t()) :: Ecto.Query.t()
|
||||||
|
def for_user_query(user_id) do
|
||||||
|
Bookmark
|
||||||
|
|> where(user_id: ^user_id)
|
||||||
|
|> join(:inner, [b], activity in assoc(b, :activity))
|
||||||
|
|> preload([b, a], activity: a)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec destroy(FlakeId.t(), FlakeId.t()) :: {:ok, Bookmark.t()} | {:error, Changeset.t()}
|
||||||
|
def destroy(user_id, activity_id) do
|
||||||
|
from(b in Bookmark,
|
||||||
|
where: b.user_id == ^user_id,
|
||||||
|
where: b.activity_id == ^activity_id
|
||||||
|
)
|
||||||
|
|> Repo.one()
|
||||||
|
|> Repo.delete()
|
||||||
|
end
|
||||||
|
end
|
|
@ -10,6 +10,7 @@ defmodule Pleroma.User do
|
||||||
|
|
||||||
alias Comeonin.Pbkdf2
|
alias Comeonin.Pbkdf2
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Bookmark
|
||||||
alias Pleroma.Formatter
|
alias Pleroma.Formatter
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
|
@ -53,8 +54,8 @@ defmodule Pleroma.User do
|
||||||
field(:search_rank, :float, virtual: true)
|
field(:search_rank, :float, virtual: true)
|
||||||
field(:search_type, :integer, virtual: true)
|
field(:search_type, :integer, virtual: true)
|
||||||
field(:tags, {:array, :string}, default: [])
|
field(:tags, {:array, :string}, default: [])
|
||||||
field(:bookmarks, {:array, :string}, default: [])
|
|
||||||
field(:last_refreshed_at, :naive_datetime_usec)
|
field(:last_refreshed_at, :naive_datetime_usec)
|
||||||
|
has_many(:bookmarks, Bookmark)
|
||||||
has_many(:notifications, Notification)
|
has_many(:notifications, Notification)
|
||||||
has_many(:registrations, Registration)
|
has_many(:registrations, Registration)
|
||||||
embeds_one(:info, Pleroma.User.Info)
|
embeds_one(:info, Pleroma.User.Info)
|
||||||
|
@ -1379,22 +1380,6 @@ defmodule Pleroma.User do
|
||||||
updated_user
|
updated_user
|
||||||
end
|
end
|
||||||
|
|
||||||
def bookmark(%User{} = user, status_id) do
|
|
||||||
bookmarks = Enum.uniq(user.bookmarks ++ [status_id])
|
|
||||||
update_bookmarks(user, bookmarks)
|
|
||||||
end
|
|
||||||
|
|
||||||
def unbookmark(%User{} = user, status_id) do
|
|
||||||
bookmarks = Enum.uniq(user.bookmarks -- [status_id])
|
|
||||||
update_bookmarks(user, bookmarks)
|
|
||||||
end
|
|
||||||
|
|
||||||
def update_bookmarks(%User{} = user, bookmarks) do
|
|
||||||
user
|
|
||||||
|> change(%{bookmarks: bookmarks})
|
|
||||||
|> update_and_set_cache
|
|
||||||
end
|
|
||||||
|
|
||||||
defp normalize_tags(tags) do
|
defp normalize_tags(tags) do
|
||||||
[tags]
|
[tags]
|
||||||
|> List.flatten()
|
|> List.flatten()
|
||||||
|
|
|
@ -6,6 +6,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
alias Ecto.Changeset
|
alias Ecto.Changeset
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Bookmark
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
alias Pleroma.Filter
|
alias Pleroma.Filter
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
|
@ -279,6 +280,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||||
|> ActivityPub.contain_timeline(user)
|
|> ActivityPub.contain_timeline(user)
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
|
|
||||||
|
user = Repo.preload(user, :bookmarks)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> add_link_headers(:home_timeline, activities)
|
|> add_link_headers(:home_timeline, activities)
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|
@ -297,6 +300,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||||
|> ActivityPub.fetch_public_activities()
|
|> ActivityPub.fetch_public_activities()
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
|
|
||||||
|
user = Repo.preload(user, :bookmarks)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> add_link_headers(:public_timeline, activities, false, %{"local" => local_only})
|
|> add_link_headers(:public_timeline, activities, false, %{"local" => local_only})
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|
@ -304,7 +309,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_statuses(%{assigns: %{user: reading_user}} = conn, params) do
|
def user_statuses(%{assigns: %{user: reading_user}} = conn, params) do
|
||||||
with %User{} = user <- User.get_cached_by_id(params["id"]) do
|
with %User{} = user <- User.get_cached_by_id(params["id"]),
|
||||||
|
reading_user <- Repo.preload(reading_user, :bookmarks) do
|
||||||
activities = ActivityPub.fetch_user_activities(user, reading_user, params)
|
activities = ActivityPub.fetch_user_activities(user, reading_user, params)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
@ -331,6 +337,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||||
|> ActivityPub.fetch_activities_query(params)
|
|> ActivityPub.fetch_activities_query(params)
|
||||||
|> Pagination.fetch_paginated(params)
|
|> Pagination.fetch_paginated(params)
|
||||||
|
|
||||||
|
user = Repo.preload(user, :bookmarks)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> add_link_headers(:dm_timeline, activities)
|
|> add_link_headers(:dm_timeline, activities)
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|
@ -548,7 +556,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||||
%Object{} = object <- Object.normalize(activity),
|
%Object{} = object <- Object.normalize(activity),
|
||||||
%User{} = user <- User.get_cached_by_nickname(user.nickname),
|
%User{} = user <- User.get_cached_by_nickname(user.nickname),
|
||||||
true <- Visibility.visible_for_user?(activity, user),
|
true <- Visibility.visible_for_user?(activity, user),
|
||||||
{:ok, user} <- User.bookmark(user, object.data["id"]) do
|
{:ok, _bookmark} <- Bookmark.create(user.id, activity.id) do
|
||||||
|
user = Repo.preload(user, :bookmarks)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
||||||
|
@ -560,7 +570,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||||
%Object{} = object <- Object.normalize(activity),
|
%Object{} = object <- Object.normalize(activity),
|
||||||
%User{} = user <- User.get_cached_by_nickname(user.nickname),
|
%User{} = user <- User.get_cached_by_nickname(user.nickname),
|
||||||
true <- Visibility.visible_for_user?(activity, user),
|
true <- Visibility.visible_for_user?(activity, user),
|
||||||
{:ok, user} <- User.unbookmark(user, object.data["id"]) do
|
{:ok, _bookmark} <- Bookmark.destroy(user.id, activity.id) do
|
||||||
|
user = Repo.preload(user, :bookmarks)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
||||||
|
@ -1124,15 +1136,20 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def bookmarks(%{assigns: %{user: user}} = conn, _) do
|
def bookmarks(%{assigns: %{user: user}} = conn, params) do
|
||||||
user = User.get_cached_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
|
user = Repo.preload(user, :bookmarks)
|
||||||
|
|
||||||
|
bookmarks =
|
||||||
|
Bookmark.for_user_query(user.id)
|
||||||
|
|> Pagination.fetch_paginated(params)
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
user.bookmarks
|
bookmarks
|
||||||
|> Enum.map(fn id -> Activity.get_create_by_object_ap_id(id) end)
|
|> Enum.map(fn b -> b.activity end)
|
||||||
|> Enum.reverse()
|
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
|> add_link_headers(:bookmarks, bookmarks)
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|> render("index.json", %{activities: activities, for: user, as: :activity})
|
|> render("index.json", %{activities: activities, for: user, as: :activity})
|
||||||
end
|
end
|
||||||
|
@ -1238,6 +1255,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||||
|> ActivityPub.fetch_activities_bounded(following, params)
|
|> ActivityPub.fetch_activities_bounded(following, params)
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
|
|
||||||
|
user = Repo.preload(user, :bookmarks)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|> render("index.json", %{activities: activities, for: user, as: :activity})
|
|> render("index.json", %{activities: activities, for: user, as: :activity})
|
||||||
|
|
|
@ -148,7 +148,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
||||||
|
|
||||||
favorited = opts[:for] && opts[:for].ap_id in (object.data["likes"] || [])
|
favorited = opts[:for] && opts[:for].ap_id in (object.data["likes"] || [])
|
||||||
|
|
||||||
bookmarked = opts[:for] && object.data["id"] in opts[:for].bookmarks
|
bookmarked =
|
||||||
|
opts[:for] && Ecto.assoc_loaded?(opts[:for].bookmarks) &&
|
||||||
|
Enum.any?(opts[:for].bookmarks, fn b -> b.activity_id == activity.id end)
|
||||||
|
|
||||||
attachment_data = object.data["attachment"] || []
|
attachment_data = object.data["attachment"] || []
|
||||||
attachments = render_many(attachment_data, StatusView, "attachment.json", as: :attachment)
|
attachments = render_many(attachment_data, StatusView, "attachment.json", as: :attachment)
|
||||||
|
|
14
priv/repo/migrations/20190413082658_create_bookmarks.exs
Normal file
14
priv/repo/migrations/20190413082658_create_bookmarks.exs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.CreateBookmarks do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
create table(:bookmarks) do
|
||||||
|
add(:user_id, references(:users, type: :uuid, on_delete: :delete_all))
|
||||||
|
add(:activity_id, references(:activities, type: :uuid, on_delete: :delete_all))
|
||||||
|
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
|
||||||
|
create(unique_index(:bookmarks, [:user_id, :activity_id]))
|
||||||
|
end
|
||||||
|
end
|
37
test/bookmark_test.exs
Normal file
37
test/bookmark_test.exs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
defmodule Pleroma.BookmarkTest do
|
||||||
|
use Pleroma.DataCase
|
||||||
|
import Pleroma.Factory
|
||||||
|
alias Pleroma.Bookmark
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
|
describe "create/2" do
|
||||||
|
test "with valid params" do
|
||||||
|
user = insert(:user)
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "Some cool information"})
|
||||||
|
{:ok, bookmark} = Bookmark.create(user.id, activity.id)
|
||||||
|
assert bookmark.user_id == user.id
|
||||||
|
assert bookmark.activity_id == activity.id
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with invalid params" do
|
||||||
|
{:error, changeset} = Bookmark.create(nil, "")
|
||||||
|
refute changeset.valid?
|
||||||
|
|
||||||
|
assert changeset.errors == [
|
||||||
|
user_id: {"can't be blank", [validation: :required]},
|
||||||
|
activity_id: {"can't be blank", [validation: :required]}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "destroy/2" do
|
||||||
|
test "with valid params" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "Some cool information"})
|
||||||
|
{:ok, _bookmark} = Bookmark.create(user.id, activity.id)
|
||||||
|
|
||||||
|
{:ok, _deleted_bookmark} = Bookmark.destroy(user.id, activity.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1125,33 +1125,6 @@ defmodule Pleroma.UserTest do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "bookmarks" do
|
|
||||||
user = insert(:user)
|
|
||||||
|
|
||||||
{:ok, activity1} =
|
|
||||||
CommonAPI.post(user, %{
|
|
||||||
"status" => "heweoo!"
|
|
||||||
})
|
|
||||||
|
|
||||||
id1 = Object.normalize(activity1).data["id"]
|
|
||||||
|
|
||||||
{:ok, activity2} =
|
|
||||||
CommonAPI.post(user, %{
|
|
||||||
"status" => "heweoo!"
|
|
||||||
})
|
|
||||||
|
|
||||||
id2 = Object.normalize(activity2).data["id"]
|
|
||||||
|
|
||||||
assert {:ok, user_state1} = User.bookmark(user, id1)
|
|
||||||
assert user_state1.bookmarks == [id1]
|
|
||||||
|
|
||||||
assert {:ok, user_state2} = User.unbookmark(user, id1)
|
|
||||||
assert user_state2.bookmarks == []
|
|
||||||
|
|
||||||
assert {:ok, user_state3} = User.bookmark(user, id2)
|
|
||||||
assert user_state3.bookmarks == [id2]
|
|
||||||
end
|
|
||||||
|
|
||||||
test "follower count is updated when a follower is blocked" do
|
test "follower count is updated when a follower is blocked" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
follower = insert(:user)
|
follower = insert(:user)
|
||||||
|
|
Loading…
Reference in a new issue