Merge pull request 'Various bookmark folders-related improvements' (#7829) from mkljczk/pleroma:bookmark-folders into develop

Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7829
This commit is contained in:
nicole mikołajczyk 2026-03-06 16:50:30 +00:00
commit 40bc79e5ce
8 changed files with 23 additions and 30 deletions

View file

@ -0,0 +1 @@
Various bookmark folders-related improvements

View file

@ -74,7 +74,7 @@ Pleroma does not process remote images and therefore cannot include fields such
The `GET /api/v1/bookmarks` endpoint accepts optional parameter `folder_id` for bookmark folder ID. The `GET /api/v1/bookmarks` endpoint accepts optional parameter `folder_id` for bookmark folder ID.
The `POST /api/v1/statuses/:id/bookmark` endpoint accepts optional parameter `folder_id` for bookmark folder ID. The `POST /api/v1/statuses/:id/bookmark` endpoint accepts optional parameter `folder_id` for bookmark folder ID. Bookmarking an already bookmarked post will update the folder association, or remove it if `folder_id` is omitted or `null`.
## Accounts ## Accounts

View file

@ -19,7 +19,7 @@ defmodule Pleroma.Bookmark do
schema "bookmarks" do schema "bookmarks" do
belongs_to(:user, User, type: FlakeId.Ecto.CompatType) belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
belongs_to(:activity, Activity, type: FlakeId.Ecto.CompatType) belongs_to(:activity, Activity, type: FlakeId.Ecto.CompatType)
belongs_to(:folder, BookmarkFolder, type: FlakeId.Ecto.CompatType) belongs_to(:folder, BookmarkFolder, type: FlakeId.Ecto.Type)
timestamps() timestamps()
end end
@ -38,7 +38,7 @@ defmodule Pleroma.Bookmark do
|> validate_required([:user_id, :activity_id]) |> validate_required([:user_id, :activity_id])
|> unique_constraint(:activity_id, name: :bookmarks_user_id_activity_id_index) |> unique_constraint(:activity_id, name: :bookmarks_user_id_activity_id_index)
|> Repo.insert( |> Repo.insert(
on_conflict: [set: [folder_id: folder_id]], on_conflict: [set: [folder_id: folder_id, updated_at: NaiveDateTime.utc_now()]],
conflict_target: [:user_id, :activity_id] conflict_target: [:user_id, :activity_id]
) )
end end
@ -76,11 +76,4 @@ defmodule Pleroma.Bookmark do
|> Repo.one() |> Repo.one()
|> Repo.delete() |> Repo.delete()
end end
def set_folder(bookmark, folder_id) do
bookmark
|> cast(%{folder_id: folder_id}, [:folder_id])
|> validate_required([:folder_id])
|> Repo.update()
end
end end

View file

@ -14,7 +14,7 @@ defmodule Pleroma.BookmarkFolder do
alias Pleroma.User alias Pleroma.User
@type t :: %__MODULE__{} @type t :: %__MODULE__{}
@primary_key {:id, FlakeId.Ecto.CompatType, autogenerate: true} @primary_key {:id, FlakeId.Ecto.Type, autogenerate: true}
schema "bookmark_folders" do schema "bookmark_folders" do
field(:name, :string) field(:name, :string)

View file

@ -15,12 +15,18 @@ defmodule Pleroma.Web.ApiSpec.Schemas.BookmarkFolder do
properties: %{ properties: %{
id: FlakeID, id: FlakeID,
name: %Schema{type: :string, description: "Folder name"}, name: %Schema{type: :string, description: "Folder name"},
emoji: %Schema{type: :string, description: "Folder emoji", nullable: true} emoji: %Schema{type: :string, description: "Folder emoji", nullable: true},
emoji_url: %Schema{
type: :string,
description: "URL of the folder emoji if it's a custom emoji, null otherwise",
nullable: true
}
}, },
example: %{ example: %{
"id" => "9toJCu5YZW7O7gfvH6", "id" => "9toJCu5YZW7O7gfvH6",
"name" => "Read later", "name" => "Read later",
"emoji" => nil "emoji" => nil,
"emoji_url" => nil
} }
}) })
end end

View file

@ -81,10 +81,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action in [:pin, :unpin]) plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action in [:pin, :unpin])
# Note: scope not present in Mastodon: read:bookmarks
plug(OAuthScopesPlug, %{scopes: ["read:bookmarks"]} when action == :bookmarks) plug(OAuthScopesPlug, %{scopes: ["read:bookmarks"]} when action == :bookmarks)
# Note: scope not present in Mastodon: write:bookmarks
plug( plug(
OAuthScopesPlug, OAuthScopesPlug,
%{scopes: ["write:bookmarks"]} when action in [:bookmark, :unbookmark] %{scopes: ["write:bookmarks"]} when action in [:bookmark, :unbookmark]

View file

@ -10,10 +10,8 @@ defmodule Pleroma.Web.PleromaAPI.BookmarkFolderController do
plug(Pleroma.Web.ApiSpec.CastAndValidate) plug(Pleroma.Web.ApiSpec.CastAndValidate)
# Note: scope not present in Mastodon: read:bookmarks
plug(OAuthScopesPlug, %{scopes: ["read:bookmarks"]} when action == :index) plug(OAuthScopesPlug, %{scopes: ["read:bookmarks"]} when action == :index)
# Note: scope not present in Mastodon: write:bookmarks
plug( plug(
OAuthScopesPlug, OAuthScopesPlug,
%{scopes: ["write:bookmarks"]} when action in [:create, :update, :delete] %{scopes: ["write:bookmarks"]} when action in [:create, :update, :delete]

View file

@ -9,11 +9,13 @@ defmodule Pleroma.Web.PleromaAPI.BookmarkFolderView do
alias Pleroma.Emoji alias Pleroma.Emoji
def render("show.json", %{folder: %BookmarkFolder{} = folder}) do def render("show.json", %{folder: %BookmarkFolder{} = folder}) do
{emoji, emoji_url} = get_emoji(folder.emoji)
%{ %{
id: folder.id |> to_string(), id: folder.id |> to_string(),
name: folder.name, name: folder.name,
emoji: folder.emoji, emoji: emoji,
emoji_url: get_emoji_url(folder.emoji) emoji_url: emoji_url
} }
end end
@ -21,20 +23,15 @@ defmodule Pleroma.Web.PleromaAPI.BookmarkFolderView do
render_many(folders, __MODULE__, "show.json", Map.delete(opts, :folders)) render_many(folders, __MODULE__, "show.json", Map.delete(opts, :folders))
end end
defp get_emoji_url(nil) do defp get_emoji(nil), do: {nil, nil}
nil
end
defp get_emoji_url(emoji) do defp get_emoji(emoji) do
if Emoji.unicode?(emoji) do if Emoji.unicode?(emoji) do
nil {emoji, nil}
else else
emoji = Emoji.get(emoji) case Emoji.get(emoji) do
nil -> {nil, nil}
if emoji != nil do emoji_data -> {emoji, Emoji.local_url(emoji_data.file)}
Emoji.local_url(emoji.file)
else
nil
end end
end end
end end