Merge remote-tracking branch 'pleroma/develop' into merge-pleroma
This commit is contained in:
commit
2427bf4e50
19 changed files with 668 additions and 69 deletions
|
@ -2,15 +2,16 @@
|
|||
|
||||
{! backend/installation/otp_vs_from_source.include !}
|
||||
|
||||
This guide covers a installation using an OTP release. To install Pleroma from source, please check out the corresponding guide for your distro.
|
||||
This guide covers a installation using OTP releases as built by the Pleroma project, it is meant as a fallback to distribution packages/recipes which are the preferred installation method.
|
||||
To install Pleroma from source, please check out the corresponding guide for your distro.
|
||||
|
||||
## Pre-requisites
|
||||
* A machine running Linux with GNU (e.g. Debian, Ubuntu) or musl (e.g. Alpine) libc and `x86_64`, `aarch64` or `armv7l` CPU, you have root access to. If you are not sure if it's compatible see [Detecting flavour section](#detecting-flavour) below
|
||||
* A machine you have root access to running Debian GNU/Linux or compatible (eg. Ubuntu), or Alpine on `x86_64`, `aarch64` or `armv7l` CPU. If you are not sure what you are running see [Detecting flavour section](#detecting-flavour) below
|
||||
* A (sub)domain pointed to the machine
|
||||
|
||||
You will be running commands as root. If you aren't root already, please elevate your privileges by executing `sudo su`/`su`.
|
||||
You will be running commands as root. If you aren't root already, please elevate your privileges by executing `sudo -i`/`su`.
|
||||
|
||||
While in theory OTP releases are possbile to install on any compatible machine, for the sake of simplicity this guide focuses only on Debian/Ubuntu and Alpine.
|
||||
Similarly to other binaries, OTP releases tend to be only compatible with the distro they are built on, as such this guide focuses only on Debian/Ubuntu and Alpine.
|
||||
|
||||
### Detecting flavour
|
||||
|
||||
|
@ -19,7 +20,7 @@ Paste the following into the shell:
|
|||
arch="$(uname -m)";if [ "$arch" = "x86_64" ];then arch="amd64";elif [ "$arch" = "armv7l" ];then arch="arm";elif [ "$arch" = "aarch64" ];then arch="arm64";else echo "Unsupported arch: $arch">&2;fi;if getconf GNU_LIBC_VERSION>/dev/null;then libc_postfix="";elif [ "$(ldd 2>&1|head -c 9)" = "musl libc" ];then libc_postfix="-musl";elif [ "$(find /lib/libc.musl*|wc -l)" ];then libc_postfix="-musl";else echo "Unsupported libc">&2;fi;echo "$arch$libc_postfix"
|
||||
```
|
||||
|
||||
If your platform is supported the output will contain the flavour string, you will need it later. If not, this just means that we don't build releases for your platform, you can still try installing from source.
|
||||
This should give your flavour string. If not this just means that we don't build releases for your platform, you can still try installing from source.
|
||||
|
||||
### Installing the required packages
|
||||
|
||||
|
|
|
@ -51,6 +51,8 @@ def reload do
|
|||
@doc "Returns the path of the emoji `name`."
|
||||
@spec get(String.t()) :: String.t() | nil
|
||||
def get(name) do
|
||||
name = maybe_strip_name(name)
|
||||
|
||||
case :ets.lookup(@ets, name) do
|
||||
[{_, path}] -> path
|
||||
_ -> nil
|
||||
|
@ -139,6 +141,57 @@ def is_unicode_emoji?(unquote(emoji)), do: true
|
|||
|
||||
def is_unicode_emoji?(_), do: false
|
||||
|
||||
@emoji_regex ~r/:[A-Za-z0-9_-]+(@.+)?:/
|
||||
|
||||
def is_custom_emoji?(s) when is_binary(s), do: Regex.match?(@emoji_regex, s)
|
||||
|
||||
def is_custom_emoji?(_), do: false
|
||||
|
||||
def maybe_strip_name(name) when is_binary(name), do: String.trim(name, ":")
|
||||
|
||||
def maybe_strip_name(name), do: name
|
||||
|
||||
def maybe_quote(name) when is_binary(name) do
|
||||
if is_unicode_emoji?(name) do
|
||||
name
|
||||
else
|
||||
if String.starts_with?(name, ":") do
|
||||
name
|
||||
else
|
||||
":#{name}:"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def maybe_quote(name), do: name
|
||||
|
||||
def emoji_url(%{"type" => "EmojiReact", "content" => _, "tag" => []}), do: nil
|
||||
|
||||
def emoji_url(%{"type" => "EmojiReact", "content" => emoji, "tag" => tags}) do
|
||||
emoji = maybe_strip_name(emoji)
|
||||
|
||||
tag =
|
||||
tags
|
||||
|> Enum.find(fn tag ->
|
||||
tag["type"] == "Emoji" && !is_nil(tag["name"]) && tag["name"] == emoji
|
||||
end)
|
||||
|
||||
if is_nil(tag) do
|
||||
nil
|
||||
else
|
||||
tag
|
||||
|> Map.get("icon")
|
||||
|> Map.get("url")
|
||||
end
|
||||
end
|
||||
|
||||
def emoji_url(_), do: nil
|
||||
|
||||
def emoji_name_with_instance(name, url) do
|
||||
url = url |> URI.parse() |> Map.get(:host)
|
||||
"#{name}@#{url}"
|
||||
end
|
||||
|
||||
emoji_qualification_map =
|
||||
emojis
|
||||
|> Enum.filter(&String.contains?(&1, "\uFE0F"))
|
||||
|
|
|
@ -16,6 +16,7 @@ defmodule Pleroma.Web.ActivityPub.Builder do
|
|||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Web.ActivityPub.Visibility
|
||||
alias Pleroma.Web.CommonAPI.ActivityDraft
|
||||
alias Pleroma.Web.Endpoint
|
||||
|
||||
require Pleroma.Constants
|
||||
|
||||
|
@ -66,13 +67,87 @@ def follow(follower, followed) do
|
|||
{:ok, data, []}
|
||||
end
|
||||
|
||||
defp unicode_emoji_react(_object, data, emoji) do
|
||||
data
|
||||
|> Map.put("content", emoji)
|
||||
|> Map.put("type", "EmojiReact")
|
||||
end
|
||||
|
||||
defp add_emoji_content(data, emoji, url) do
|
||||
tag = [
|
||||
%{
|
||||
"id" => url,
|
||||
"type" => "Emoji",
|
||||
"name" => Emoji.maybe_quote(emoji),
|
||||
"icon" => %{
|
||||
"type" => "Image",
|
||||
"url" => url
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
data
|
||||
|> Map.put("content", Emoji.maybe_quote(emoji))
|
||||
|> Map.put("type", "EmojiReact")
|
||||
|> Map.put("tag", tag)
|
||||
end
|
||||
|
||||
defp remote_custom_emoji_react(
|
||||
%{data: %{"reactions" => existing_reactions}},
|
||||
data,
|
||||
emoji
|
||||
) do
|
||||
[emoji_code, instance] = String.split(Emoji.maybe_strip_name(emoji), "@")
|
||||
|
||||
matching_reaction =
|
||||
Enum.find(
|
||||
existing_reactions,
|
||||
fn [name, _, url] ->
|
||||
if url != nil do
|
||||
url = URI.parse(url)
|
||||
url.host == instance && name == emoji_code
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
if matching_reaction do
|
||||
[name, _, url] = matching_reaction
|
||||
add_emoji_content(data, name, url)
|
||||
else
|
||||
{:error, "Could not react"}
|
||||
end
|
||||
end
|
||||
|
||||
defp remote_custom_emoji_react(_object, _data, _emoji) do
|
||||
{:error, "Could not react"}
|
||||
end
|
||||
|
||||
defp local_custom_emoji_react(data, emoji) do
|
||||
with %{file: path} = emojo <- Emoji.get(emoji) do
|
||||
url = "#{Endpoint.url()}#{path}"
|
||||
add_emoji_content(data, emojo.code, url)
|
||||
else
|
||||
_ -> {:error, "Emoji does not exist"}
|
||||
end
|
||||
end
|
||||
|
||||
defp custom_emoji_react(object, data, emoji) do
|
||||
if String.contains?(emoji, "@") do
|
||||
remote_custom_emoji_react(object, data, emoji)
|
||||
else
|
||||
local_custom_emoji_react(data, emoji)
|
||||
end
|
||||
end
|
||||
|
||||
@spec emoji_react(User.t(), Object.t(), String.t()) :: {:ok, map(), keyword()}
|
||||
def emoji_react(actor, object, emoji) do
|
||||
with {:ok, data, meta} <- object_action(actor, object) do
|
||||
data =
|
||||
data
|
||||
|> Map.put("content", emoji)
|
||||
|> Map.put("type", "EmojiReact")
|
||||
if Emoji.is_unicode_emoji?(emoji) do
|
||||
unicode_emoji_react(object, data, emoji)
|
||||
else
|
||||
custom_emoji_react(object, data, emoji)
|
||||
end
|
||||
|
||||
{:ok, data, meta}
|
||||
end
|
||||
|
|
|
@ -5,8 +5,10 @@
|
|||
defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do
|
||||
use Ecto.Schema
|
||||
|
||||
alias Pleroma.Emoji
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.TagValidator
|
||||
|
||||
import Ecto.Changeset
|
||||
import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
||||
|
@ -19,6 +21,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do
|
|||
import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields
|
||||
message_fields()
|
||||
activity_fields()
|
||||
embeds_many(:tag, TagValidator)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -43,7 +46,8 @@ def cast_data(data) do
|
|||
|
||||
def changeset(struct, data) do
|
||||
struct
|
||||
|> cast(data, __schema__(:fields))
|
||||
|> cast(data, __schema__(:fields) -- [:tag])
|
||||
|> cast_embed(:tag)
|
||||
end
|
||||
|
||||
defp fix(data) do
|
||||
|
@ -53,12 +57,16 @@ defp fix(data) do
|
|||
|> CommonFixes.fix_actor()
|
||||
|> CommonFixes.fix_activity_addressing()
|
||||
|
||||
with %Object{} = object <- Object.normalize(data["object"]) do
|
||||
data
|
||||
|> CommonFixes.fix_activity_context(object)
|
||||
|> CommonFixes.fix_object_action_recipients(object)
|
||||
else
|
||||
_ -> data
|
||||
data = Map.put_new(data, "tag", [])
|
||||
|
||||
case Object.normalize(data["object"]) do
|
||||
%Object{} = object ->
|
||||
data
|
||||
|> CommonFixes.fix_activity_context(object)
|
||||
|> CommonFixes.fix_object_action_recipients(object)
|
||||
|
||||
_ ->
|
||||
data
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -82,11 +90,31 @@ defp fix_emoji_qualification(data), do: data
|
|||
defp validate_emoji(cng) do
|
||||
content = get_field(cng, :content)
|
||||
|
||||
if Pleroma.Emoji.is_unicode_emoji?(content) do
|
||||
if Emoji.is_unicode_emoji?(content) || Emoji.is_custom_emoji?(content) do
|
||||
cng
|
||||
else
|
||||
cng
|
||||
|> add_error(:content, "must be a single character emoji")
|
||||
|> add_error(:content, "is not a valid emoji")
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_validate_tag_presence(cng) do
|
||||
content = get_field(cng, :content)
|
||||
|
||||
if Emoji.is_unicode_emoji?(content) do
|
||||
cng
|
||||
else
|
||||
tag = get_field(cng, :tag)
|
||||
emoji_name = Emoji.maybe_strip_name(content)
|
||||
|
||||
case tag do
|
||||
[%{name: ^emoji_name, type: "Emoji", icon: %{url: _}}] ->
|
||||
cng
|
||||
|
||||
_ ->
|
||||
cng
|
||||
|> add_error(:tag, "does not contain an Emoji tag")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -97,5 +125,6 @@ defp validate_data(data_cng) do
|
|||
|> validate_actor_presence()
|
||||
|> validate_object_presence()
|
||||
|> validate_emoji()
|
||||
|> maybe_validate_tag_presence()
|
||||
end
|
||||
end
|
||||
|
|
|
@ -332,21 +332,29 @@ def update_element_in_object(property, element, object, count \\ nil) do
|
|||
{:ok, Object.t()} | {:error, Ecto.Changeset.t()}
|
||||
|
||||
def add_emoji_reaction_to_object(
|
||||
%Activity{data: %{"content" => emoji, "actor" => actor}},
|
||||
%Activity{data: %{"content" => emoji, "actor" => actor}} = activity,
|
||||
object
|
||||
) do
|
||||
reactions = get_cached_emoji_reactions(object)
|
||||
emoji = Pleroma.Emoji.maybe_strip_name(emoji)
|
||||
url = maybe_emoji_url(emoji, activity)
|
||||
|
||||
new_reactions =
|
||||
case Enum.find_index(reactions, fn [candidate, _] -> emoji == candidate end) do
|
||||
case Enum.find_index(reactions, fn [candidate, _, candidate_url] ->
|
||||
if is_nil(candidate_url) do
|
||||
emoji == candidate
|
||||
else
|
||||
url == candidate_url
|
||||
end
|
||||
end) do
|
||||
nil ->
|
||||
reactions ++ [[emoji, [actor]]]
|
||||
reactions ++ [[emoji, [actor], url]]
|
||||
|
||||
index ->
|
||||
List.update_at(
|
||||
reactions,
|
||||
index,
|
||||
fn [emoji, users] -> [emoji, Enum.uniq([actor | users])] end
|
||||
fn [emoji, users, url] -> [emoji, Enum.uniq([actor | users]), url] end
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -355,18 +363,40 @@ def add_emoji_reaction_to_object(
|
|||
update_element_in_object("reaction", new_reactions, object, count)
|
||||
end
|
||||
|
||||
defp maybe_emoji_url(
|
||||
name,
|
||||
%Activity{
|
||||
data: %{
|
||||
"tag" => [
|
||||
%{"type" => "Emoji", "name" => name, "icon" => %{"url" => url}}
|
||||
]
|
||||
}
|
||||
}
|
||||
),
|
||||
do: url
|
||||
|
||||
defp maybe_emoji_url(_, _), do: nil
|
||||
|
||||
def emoji_count(reactions_list) do
|
||||
Enum.reduce(reactions_list, 0, fn [_, users], acc -> acc + length(users) end)
|
||||
Enum.reduce(reactions_list, 0, fn [_, users, _], acc -> acc + length(users) end)
|
||||
end
|
||||
|
||||
def remove_emoji_reaction_from_object(
|
||||
%Activity{data: %{"content" => emoji, "actor" => actor}},
|
||||
%Activity{data: %{"content" => emoji, "actor" => actor}} = activity,
|
||||
object
|
||||
) do
|
||||
emoji = Pleroma.Emoji.maybe_strip_name(emoji)
|
||||
reactions = get_cached_emoji_reactions(object)
|
||||
url = maybe_emoji_url(emoji, activity)
|
||||
|
||||
new_reactions =
|
||||
case Enum.find_index(reactions, fn [candidate, _] -> emoji == candidate end) do
|
||||
case Enum.find_index(reactions, fn [candidate, _, candidate_url] ->
|
||||
if is_nil(candidate_url) do
|
||||
emoji == candidate
|
||||
else
|
||||
url == candidate_url
|
||||
end
|
||||
end) do
|
||||
nil ->
|
||||
reactions
|
||||
|
||||
|
@ -374,9 +404,9 @@ def remove_emoji_reaction_from_object(
|
|||
List.update_at(
|
||||
reactions,
|
||||
index,
|
||||
fn [emoji, users] -> [emoji, List.delete(users, actor)] end
|
||||
fn [emoji, users, url] -> [emoji, List.delete(users, actor), url] end
|
||||
)
|
||||
|> Enum.reject(fn [_, users] -> Enum.empty?(users) end)
|
||||
|> Enum.reject(fn [_, users, _] -> Enum.empty?(users) end)
|
||||
end
|
||||
|
||||
count = emoji_count(new_reactions)
|
||||
|
@ -538,17 +568,37 @@ def fetch_latest_undo(%User{ap_id: ap_id}) do
|
|||
|
||||
def get_latest_reaction(internal_activity_id, %{ap_id: ap_id}, emoji) do
|
||||
%{data: %{"object" => object_ap_id}} = Activity.get_by_id(internal_activity_id)
|
||||
emoji = Pleroma.Emoji.maybe_quote(emoji)
|
||||
|
||||
"EmojiReact"
|
||||
|> Activity.Queries.by_type()
|
||||
|> where(actor: ^ap_id)
|
||||
|> where([activity], fragment("?->>'content' = ?", activity.data, ^emoji))
|
||||
|> custom_emoji_discriminator(emoji)
|
||||
|> Activity.Queries.by_object_id(object_ap_id)
|
||||
|> order_by([activity], fragment("? desc nulls last", activity.id))
|
||||
|> limit(1)
|
||||
|> Repo.one()
|
||||
end
|
||||
|
||||
defp custom_emoji_discriminator(query, emoji) do
|
||||
if String.contains?(emoji, "@") do
|
||||
stripped = Pleroma.Emoji.maybe_strip_name(emoji)
|
||||
[name, domain] = String.split(stripped, "@")
|
||||
domain_pattern = "%/" <> domain <> "/%"
|
||||
emoji_pattern = Pleroma.Emoji.maybe_quote(name)
|
||||
|
||||
query
|
||||
|> where([activity], fragment("?->>'content' = ?
|
||||
AND EXISTS (
|
||||
SELECT FROM jsonb_array_elements(?->'tag') elem
|
||||
WHERE elem->>'id' ILIKE ?
|
||||
)", activity.data, ^emoji_pattern, activity.data, ^domain_pattern))
|
||||
else
|
||||
query
|
||||
|> where([activity], fragment("?->>'content' = ?", activity.data, ^emoji))
|
||||
end
|
||||
end
|
||||
|
||||
#### Announce-related helpers
|
||||
|
||||
@doc """
|
||||
|
|
|
@ -162,6 +162,7 @@ def features do
|
|||
"safe_dm_mentions"
|
||||
end,
|
||||
"pleroma_emoji_reactions",
|
||||
"pleroma_custom_emoji_reactions",
|
||||
"pleroma_chat_messages",
|
||||
"email_list",
|
||||
if Config.get([:instance, :show_reactions]) do
|
||||
|
|
|
@ -17,6 +17,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
|
|||
alias Pleroma.Web.MastodonAPI.AccountView
|
||||
alias Pleroma.Web.MastodonAPI.NotificationView
|
||||
alias Pleroma.Web.MastodonAPI.StatusView
|
||||
alias Pleroma.Web.MediaProxy
|
||||
alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView
|
||||
|
||||
defp object_id_for(%{data: %{"object" => %{"id" => id}}}) when is_binary(id), do: id
|
||||
|
@ -157,7 +158,9 @@ defp put_report(response, activity) do
|
|||
end
|
||||
|
||||
defp put_emoji(response, activity) do
|
||||
Map.put(response, :emoji, activity.data["content"])
|
||||
response
|
||||
|> Map.put(:emoji, activity.data["content"])
|
||||
|> Map.put(:emoji_url, MediaProxy.url(Pleroma.Emoji.emoji_url(activity.data)))
|
||||
end
|
||||
|
||||
defp put_chat_message(response, activity, reading_user, opts) do
|
||||
|
|
|
@ -377,8 +377,8 @@ def render("show.json", %{activity: %{data: %{"object" => _object}} = activity}
|
|||
opts[:for],
|
||||
Map.get(opts, :with_muted, false)
|
||||
)
|
||||
|> Stream.map(fn {emoji, users} ->
|
||||
build_emoji_map(emoji, users, opts[:for])
|
||||
|> Stream.map(fn {emoji, users, url} ->
|
||||
build_emoji_map(emoji, users, url, opts[:for])
|
||||
end)
|
||||
|> Enum.to_list()
|
||||
|
||||
|
@ -841,11 +841,13 @@ defp pin_data(%Object{data: %{"id" => object_id}}, %User{pinned_objects: pinned_
|
|||
end
|
||||
end
|
||||
|
||||
defp build_emoji_map(emoji, users, current_user) do
|
||||
defp build_emoji_map(emoji, users, url, current_user) do
|
||||
%{
|
||||
name: emoji,
|
||||
name: Pleroma.Web.PleromaAPI.EmojiReactionView.emoji_name(emoji, url),
|
||||
count: length(users),
|
||||
me: !!(current_user && current_user.ap_id in users)
|
||||
url: MediaProxy.url(url),
|
||||
me: !!(current_user && current_user.ap_id in users),
|
||||
account_ids: Enum.map(users, fn user -> User.get_cached_by_ap_id(user).id end)
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -8,12 +8,20 @@ defmodule Pleroma.Web.Metadata.Providers.RelMe do
|
|||
|
||||
@impl Provider
|
||||
def build_tags(%{user: user}) do
|
||||
bio_tree = Floki.parse_fragment!(user.bio)
|
||||
profile_tree =
|
||||
user.bio
|
||||
|> append_fields_tag(user.fields)
|
||||
|> Floki.parse_fragment!()
|
||||
|
||||
(Floki.attribute(bio_tree, "link[rel~=me]", "href") ++
|
||||
Floki.attribute(bio_tree, "a[rel~=me]", "href"))
|
||||
(Floki.attribute(profile_tree, "link[rel~=me]", "href") ++
|
||||
Floki.attribute(profile_tree, "a[rel~=me]", "href"))
|
||||
|> Enum.map(fn link ->
|
||||
{:link, [rel: "me", href: link], []}
|
||||
end)
|
||||
end
|
||||
|
||||
defp append_fields_tag(bio, fields) do
|
||||
fields
|
||||
|> Enum.reduce(bio, fn %{"value" => v}, res -> res <> v end)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -50,29 +50,35 @@ def filter_allowed_users(reactions, user, with_muted) do
|
|||
if not with_muted, do: User.cached_muted_users_ap_ids(user), else: []
|
||||
end
|
||||
|
||||
filter_emoji = fn emoji, users ->
|
||||
filter_emoji = fn emoji, users, url ->
|
||||
case Enum.reject(users, &(&1 in exclude_ap_ids)) do
|
||||
[] -> nil
|
||||
users -> {emoji, users}
|
||||
users -> {emoji, users, url}
|
||||
end
|
||||
end
|
||||
|
||||
reactions
|
||||
|> Stream.map(fn
|
||||
[emoji, users] when is_list(users) -> filter_emoji.(emoji, users)
|
||||
{emoji, users} when is_list(users) -> filter_emoji.(emoji, users)
|
||||
[emoji, users, url] when is_list(users) -> filter_emoji.(emoji, users, url)
|
||||
{emoji, users, url} when is_list(users) -> filter_emoji.(emoji, users, url)
|
||||
{emoji, users} when is_list(users) -> filter_emoji.(emoji, users, nil)
|
||||
_ -> nil
|
||||
end)
|
||||
|> Stream.reject(&is_nil/1)
|
||||
end
|
||||
|
||||
defp filter(reactions, %{emoji: emoji}) when is_binary(emoji) do
|
||||
Enum.filter(reactions, fn [e, _] -> e == emoji end)
|
||||
Enum.filter(reactions, fn [e, _, _] -> e == emoji end)
|
||||
end
|
||||
|
||||
defp filter(reactions, _), do: reactions
|
||||
|
||||
def create(%{assigns: %{user: user}} = conn, %{id: activity_id, emoji: emoji}) do
|
||||
emoji =
|
||||
emoji
|
||||
|> Pleroma.Emoji.fully_qualify_emoji()
|
||||
|> Pleroma.Emoji.maybe_quote()
|
||||
|
||||
with {:ok, _activity} <- CommonAPI.react_with_emoji(activity_id, user, emoji) do
|
||||
activity = Activity.get_by_id(activity_id)
|
||||
|
||||
|
@ -83,6 +89,11 @@ def create(%{assigns: %{user: user}} = conn, %{id: activity_id, emoji: emoji}) d
|
|||
end
|
||||
|
||||
def delete(%{assigns: %{user: user}} = conn, %{id: activity_id, emoji: emoji}) do
|
||||
emoji =
|
||||
emoji
|
||||
|> Pleroma.Emoji.fully_qualify_emoji()
|
||||
|> Pleroma.Emoji.maybe_quote()
|
||||
|
||||
with {:ok, _activity} <- CommonAPI.unreact_with_emoji(activity_id, user, emoji) do
|
||||
activity = Activity.get_by_id(activity_id)
|
||||
|
||||
|
|
|
@ -7,17 +7,30 @@ defmodule Pleroma.Web.PleromaAPI.EmojiReactionView do
|
|||
|
||||
alias Pleroma.Web.MastodonAPI.AccountView
|
||||
|
||||
def emoji_name(emoji, nil), do: emoji
|
||||
|
||||
def emoji_name(emoji, url) do
|
||||
url = URI.parse(url)
|
||||
|
||||
if url.host == Pleroma.Web.Endpoint.host() do
|
||||
emoji
|
||||
else
|
||||
"#{emoji}@#{url.host}"
|
||||
end
|
||||
end
|
||||
|
||||
def render("index.json", %{emoji_reactions: emoji_reactions} = opts) do
|
||||
render_many(emoji_reactions, __MODULE__, "show.json", opts)
|
||||
end
|
||||
|
||||
def render("show.json", %{emoji_reaction: {emoji, user_ap_ids}, user: user}) do
|
||||
def render("show.json", %{emoji_reaction: {emoji, user_ap_ids, url}, user: user}) do
|
||||
users = fetch_users(user_ap_ids)
|
||||
|
||||
%{
|
||||
name: emoji,
|
||||
name: emoji_name(emoji, url),
|
||||
count: length(users),
|
||||
accounts: render(AccountView, "index.json", users: users, for: user),
|
||||
url: Pleroma.Web.MediaProxy.url(url),
|
||||
me: !!(user && user.ap_id in user_ap_ids)
|
||||
}
|
||||
end
|
||||
|
|
28
test/fixtures/custom-emoji-reaction.json
vendored
Normal file
28
test/fixtures/custom-emoji-reaction.json
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://w3id.org/security/v1",
|
||||
{
|
||||
"Hashtag": "as:Hashtag"
|
||||
}
|
||||
],
|
||||
"type": "Like",
|
||||
"id": "https://misskey.local.live/likes/917ocsybgp",
|
||||
"actor": "https://misskey.local.live/users/8x8yep20u2",
|
||||
"object": "https://pleroma.local.live/objects/89937a53-2692-4631-bb62-770091267391",
|
||||
"content": ":hanapog:",
|
||||
"_misskey_reaction": ":hanapog:",
|
||||
"tag": [
|
||||
{
|
||||
"id": "https://misskey.local.live/emojis/hanapog",
|
||||
"type": "Emoji",
|
||||
"name": ":hanapog:",
|
||||
"updated": "2022-06-07T12:00:05.773Z",
|
||||
"icon": {
|
||||
"type": "Image",
|
||||
"mediaType": "image/png",
|
||||
"url": "https://misskey.local.live/files/webpublic-8f8a9768-7264-4171-88d6-2356aabeadcd"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -38,16 +38,70 @@ test "it is not valid without a 'content' field", %{valid_emoji_react: valid_emo
|
|||
assert {:content, {"can't be blank", [validation: :required]}} in cng.errors
|
||||
end
|
||||
|
||||
test "it is not valid with a non-emoji content field", %{valid_emoji_react: valid_emoji_react} do
|
||||
test "it is valid when custom emoji is used", %{valid_emoji_react: valid_emoji_react} do
|
||||
without_emoji_content =
|
||||
valid_emoji_react
|
||||
|> Map.put("content", "x")
|
||||
|> Map.put("content", ":hello:")
|
||||
|> Map.put("tag", [
|
||||
%{
|
||||
"type" => "Emoji",
|
||||
"name" => ":hello:",
|
||||
"icon" => %{"url" => "http://somewhere", "type" => "Image"}
|
||||
}
|
||||
])
|
||||
|
||||
{:ok, _, _} = ObjectValidator.validate(without_emoji_content, [])
|
||||
end
|
||||
|
||||
test "it is not valid when custom emoji don't have a matching tag", %{
|
||||
valid_emoji_react: valid_emoji_react
|
||||
} do
|
||||
without_emoji_content =
|
||||
valid_emoji_react
|
||||
|> Map.put("content", ":hello:")
|
||||
|> Map.put("tag", [
|
||||
%{
|
||||
"type" => "Emoji",
|
||||
"name" => ":whoops:",
|
||||
"icon" => %{"url" => "http://somewhere", "type" => "Image"}
|
||||
}
|
||||
])
|
||||
|
||||
{:error, cng} = ObjectValidator.validate(without_emoji_content, [])
|
||||
|
||||
refute cng.valid?
|
||||
|
||||
assert {:content, {"must be a single character emoji", []}} in cng.errors
|
||||
assert {:tag, {"does not contain an Emoji tag", []}} in cng.errors
|
||||
end
|
||||
|
||||
test "it is not valid when custom emoji have no tags", %{
|
||||
valid_emoji_react: valid_emoji_react
|
||||
} do
|
||||
without_emoji_content =
|
||||
valid_emoji_react
|
||||
|> Map.put("content", ":hello:")
|
||||
|> Map.put("tag", [])
|
||||
|
||||
{:error, cng} = ObjectValidator.validate(without_emoji_content, [])
|
||||
|
||||
refute cng.valid?
|
||||
|
||||
assert {:tag, {"does not contain an Emoji tag", []}} in cng.errors
|
||||
end
|
||||
|
||||
test "it is not valid when custom emoji doesn't match a shortcode format", %{
|
||||
valid_emoji_react: valid_emoji_react
|
||||
} do
|
||||
without_emoji_content =
|
||||
valid_emoji_react
|
||||
|> Map.put("content", "hello")
|
||||
|> Map.put("tag", [])
|
||||
|
||||
{:error, cng} = ObjectValidator.validate(without_emoji_content, [])
|
||||
|
||||
refute cng.valid?
|
||||
|
||||
assert {:tag, {"does not contain an Emoji tag", []}} in cng.errors
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -453,7 +453,7 @@ test "adds the reaction to the object", %{emoji_react: emoji_react, user: user}
|
|||
object = Object.get_by_ap_id(emoji_react.data["object"])
|
||||
|
||||
assert object.data["reaction_count"] == 1
|
||||
assert ["👌", [user.ap_id]] in object.data["reactions"]
|
||||
assert ["👌", [user.ap_id], nil] in object.data["reactions"]
|
||||
end
|
||||
|
||||
test "creates a notification", %{emoji_react: emoji_react, poster: poster} do
|
||||
|
|
|
@ -34,7 +34,56 @@ test "it works for incoming emoji reactions" do
|
|||
object = Object.get_by_ap_id(data["object"])
|
||||
|
||||
assert object.data["reaction_count"] == 1
|
||||
assert match?([["👌", _]], object.data["reactions"])
|
||||
assert match?([["👌", _, nil]], object.data["reactions"])
|
||||
end
|
||||
|
||||
test "it works for incoming custom emoji reactions" do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user, local: false)
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "hello"})
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/custom-emoji-reaction.json")
|
||||
|> Jason.decode!()
|
||||
|> Map.put("object", activity.data["object"])
|
||||
|> Map.put("actor", other_user.ap_id)
|
||||
|
||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||
|
||||
assert data["actor"] == other_user.ap_id
|
||||
assert data["type"] == "EmojiReact"
|
||||
assert data["id"] == "https://misskey.local.live/likes/917ocsybgp"
|
||||
assert data["object"] == activity.data["object"]
|
||||
assert data["content"] == ":hanapog:"
|
||||
|
||||
assert data["tag"] == [
|
||||
%{
|
||||
"id" => "https://misskey.local.live/emojis/hanapog",
|
||||
"type" => "Emoji",
|
||||
"name" => "hanapog",
|
||||
"updated" => "2022-06-07T12:00:05.773Z",
|
||||
"icon" => %{
|
||||
"type" => "Image",
|
||||
"url" =>
|
||||
"https://misskey.local.live/files/webpublic-8f8a9768-7264-4171-88d6-2356aabeadcd"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
object = Object.get_by_ap_id(data["object"])
|
||||
|
||||
assert object.data["reaction_count"] == 1
|
||||
|
||||
assert match?(
|
||||
[
|
||||
[
|
||||
"hanapog",
|
||||
_,
|
||||
"https://misskey.local.live/files/webpublic-8f8a9768-7264-4171-88d6-2356aabeadcd"
|
||||
]
|
||||
],
|
||||
object.data["reactions"]
|
||||
)
|
||||
end
|
||||
|
||||
test "it works for incoming unqualified emoji reactions" do
|
||||
|
@ -65,7 +114,7 @@ test "it works for incoming unqualified emoji reactions" do
|
|||
object = Object.get_by_ap_id(data["object"])
|
||||
|
||||
assert object.data["reaction_count"] == 1
|
||||
assert match?([[^emoji, _]], object.data["reactions"])
|
||||
assert match?([[^emoji, _, _]], object.data["reactions"])
|
||||
end
|
||||
|
||||
test "it reject invalid emoji reactions" do
|
||||
|
|
|
@ -191,7 +191,47 @@ test "EmojiReact notification" do
|
|||
emoji: "☕",
|
||||
account: AccountView.render("show.json", %{user: other_user, for: user}),
|
||||
status: StatusView.render("show.json", %{activity: activity, for: user}),
|
||||
created_at: Utils.to_masto_date(notification.inserted_at)
|
||||
created_at: Utils.to_masto_date(notification.inserted_at),
|
||||
emoji_url: nil
|
||||
}
|
||||
|
||||
test_notifications_rendering([notification], user, [expected])
|
||||
end
|
||||
|
||||
test "EmojiReact custom emoji notification" do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
|
||||
note =
|
||||
insert(:note,
|
||||
user: user,
|
||||
data: %{
|
||||
"reactions" => [
|
||||
["👍", [user.ap_id], nil],
|
||||
["dinosaur", [user.ap_id], "http://localhost:4001/emoji/dino walking.gif"]
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
activity = insert(:note_activity, note: note, user: user)
|
||||
|
||||
{:ok, _activity} = CommonAPI.react_with_emoji(activity.id, other_user, "dinosaur")
|
||||
|
||||
activity = Repo.get(Activity, activity.id)
|
||||
|
||||
[notification] = Notification.for_user(user)
|
||||
|
||||
assert notification
|
||||
|
||||
expected = %{
|
||||
id: to_string(notification.id),
|
||||
pleroma: %{is_seen: false, is_muted: false},
|
||||
type: "pleroma:emoji_reaction",
|
||||
emoji: ":dinosaur:",
|
||||
account: AccountView.render("show.json", %{user: other_user, for: user}),
|
||||
status: StatusView.render("show.json", %{activity: activity, for: user}),
|
||||
created_at: Utils.to_masto_date(notification.inserted_at),
|
||||
emoji_url: "http://localhost:4001/emoji/dino walking.gif"
|
||||
}
|
||||
|
||||
test_notifications_rendering([notification], user, [expected])
|
||||
|
|
|
@ -36,16 +36,26 @@ test "has an emoji reaction list" do
|
|||
{:ok, activity} = CommonAPI.post(user, %{status: "dae cofe??"})
|
||||
|
||||
{:ok, _} = CommonAPI.react_with_emoji(activity.id, user, "☕")
|
||||
{:ok, _} = CommonAPI.react_with_emoji(activity.id, user, ":dinosaur:")
|
||||
{:ok, _} = CommonAPI.react_with_emoji(activity.id, third_user, "🍵")
|
||||
{:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
|
||||
{:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, ":dinosaur:")
|
||||
|
||||
activity = Repo.get(Activity, activity.id)
|
||||
status = StatusView.render("show.json", activity: activity)
|
||||
|
||||
assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
|
||||
|
||||
assert status[:pleroma][:emoji_reactions] == [
|
||||
%{name: "☕", count: 2, me: false},
|
||||
%{name: "🍵", count: 1, me: false}
|
||||
%{name: "☕", count: 2, me: false, url: nil, account_ids: [other_user.id, user.id]},
|
||||
%{
|
||||
count: 2,
|
||||
me: false,
|
||||
name: "dinosaur",
|
||||
url: "http://localhost:4001/emoji/dino walking.gif",
|
||||
account_ids: [other_user.id, user.id]
|
||||
},
|
||||
%{name: "🍵", count: 1, me: false, url: nil, account_ids: [third_user.id]}
|
||||
]
|
||||
|
||||
status = StatusView.render("show.json", activity: activity, for: user)
|
||||
|
@ -53,8 +63,15 @@ test "has an emoji reaction list" do
|
|||
assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
|
||||
|
||||
assert status[:pleroma][:emoji_reactions] == [
|
||||
%{name: "☕", count: 2, me: true},
|
||||
%{name: "🍵", count: 1, me: false}
|
||||
%{name: "☕", count: 2, me: true, url: nil, account_ids: [other_user.id, user.id]},
|
||||
%{
|
||||
count: 2,
|
||||
me: true,
|
||||
name: "dinosaur",
|
||||
url: "http://localhost:4001/emoji/dino walking.gif",
|
||||
account_ids: [other_user.id, user.id]
|
||||
},
|
||||
%{name: "🍵", count: 1, me: false, url: nil, account_ids: [third_user.id]}
|
||||
]
|
||||
end
|
||||
|
||||
|
@ -67,11 +84,10 @@ test "works correctly with badly formatted emojis" do
|
|||
|> Object.update_data(%{"reactions" => %{"☕" => [user.ap_id], "x" => 1}})
|
||||
|
||||
activity = Activity.get_by_id(activity.id)
|
||||
|
||||
status = StatusView.render("show.json", activity: activity, for: user)
|
||||
|
||||
assert status[:pleroma][:emoji_reactions] == [
|
||||
%{name: "☕", count: 1, me: true}
|
||||
%{name: "☕", count: 1, me: true, url: nil, account_ids: [user.id]}
|
||||
]
|
||||
end
|
||||
|
||||
|
@ -91,7 +107,7 @@ test "doesn't show reactions from muted and blocked users" do
|
|||
status = StatusView.render("show.json", activity: activity)
|
||||
|
||||
assert status[:pleroma][:emoji_reactions] == [
|
||||
%{name: "☕", count: 1, me: false}
|
||||
%{name: "☕", count: 1, me: false, url: nil, account_ids: [other_user.id]}
|
||||
]
|
||||
|
||||
status = StatusView.render("show.json", activity: activity, for: user)
|
||||
|
@ -103,19 +119,25 @@ test "doesn't show reactions from muted and blocked users" do
|
|||
status = StatusView.render("show.json", activity: activity)
|
||||
|
||||
assert status[:pleroma][:emoji_reactions] == [
|
||||
%{name: "☕", count: 2, me: false}
|
||||
%{
|
||||
name: "☕",
|
||||
count: 2,
|
||||
me: false,
|
||||
url: nil,
|
||||
account_ids: [third_user.id, other_user.id]
|
||||
}
|
||||
]
|
||||
|
||||
status = StatusView.render("show.json", activity: activity, for: user)
|
||||
|
||||
assert status[:pleroma][:emoji_reactions] == [
|
||||
%{name: "☕", count: 1, me: false}
|
||||
%{name: "☕", count: 1, me: false, url: nil, account_ids: [third_user.id]}
|
||||
]
|
||||
|
||||
status = StatusView.render("show.json", activity: activity, for: other_user)
|
||||
|
||||
assert status[:pleroma][:emoji_reactions] == [
|
||||
%{name: "☕", count: 1, me: true}
|
||||
%{name: "☕", count: 1, me: true, url: nil, account_ids: [other_user.id]}
|
||||
]
|
||||
end
|
||||
|
||||
|
|
|
@ -11,11 +11,24 @@ test "it renders all links with rel='me' from user bio" do
|
|||
bio =
|
||||
~s(<a href="https://some-link.com">https://some-link.com</a> <a rel="me" href="https://another-link.com">https://another-link.com</a> <link href="http://some.com"> <link rel="me" href="http://some3.com">)
|
||||
|
||||
user = insert(:user, %{bio: bio})
|
||||
fields = [
|
||||
%{
|
||||
"name" => "profile",
|
||||
"value" => ~S(<a rel="me" href="http://profile.com">http://profile.com</a>)
|
||||
},
|
||||
%{
|
||||
"name" => "like",
|
||||
"value" => ~S(<a href="http://cofe.io">http://cofe.io</a>)
|
||||
},
|
||||
%{"name" => "foo", "value" => "bar"}
|
||||
]
|
||||
|
||||
user = insert(:user, %{bio: bio, fields: fields})
|
||||
|
||||
assert RelMe.build_tags(%{user: user}) == [
|
||||
{:link, [rel: "me", href: "http://some3.com"], []},
|
||||
{:link, [rel: "me", href: "https://another-link.com"], []}
|
||||
{:link, [rel: "me", href: "https://another-link.com"], []},
|
||||
{:link, [rel: "me", href: "http://profile.com"], []}
|
||||
]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,23 +17,113 @@ test "PUT /api/v1/pleroma/statuses/:id/reactions/:emoji", %{conn: conn} do
|
|||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "#cofe"})
|
||||
note = insert(:note, user: user, data: %{"reactions" => [["👍", [other_user.ap_id], nil]]})
|
||||
activity = insert(:note_activity, note: note, user: user)
|
||||
|
||||
result =
|
||||
conn
|
||||
|> assign(:user, other_user)
|
||||
|> assign(:token, insert(:oauth_token, user: other_user, scopes: ["write:statuses"]))
|
||||
|> put("/api/v1/pleroma/statuses/#{activity.id}/reactions/☕")
|
||||
|> put("/api/v1/pleroma/statuses/#{activity.id}/reactions/\u26A0")
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
# We return the status, but this our implementation detail.
|
||||
assert %{"id" => id} = result
|
||||
assert to_string(activity.id) == id
|
||||
|
||||
assert result["pleroma"]["emoji_reactions"] == [
|
||||
%{"name" => "☕", "count" => 1, "me" => true}
|
||||
%{
|
||||
"name" => "👍",
|
||||
"count" => 1,
|
||||
"me" => true,
|
||||
"url" => nil,
|
||||
"account_ids" => [other_user.id]
|
||||
},
|
||||
%{
|
||||
"name" => "\u26A0\uFE0F",
|
||||
"count" => 1,
|
||||
"me" => true,
|
||||
"url" => nil,
|
||||
"account_ids" => [other_user.id]
|
||||
}
|
||||
]
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "#cofe"})
|
||||
|
||||
ObanHelpers.perform_all()
|
||||
|
||||
# Reacting with a custom emoji
|
||||
result =
|
||||
conn
|
||||
|> assign(:user, other_user)
|
||||
|> assign(:token, insert(:oauth_token, user: other_user, scopes: ["write:statuses"]))
|
||||
|> put("/api/v1/pleroma/statuses/#{activity.id}/reactions/:dinosaur:")
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert %{"id" => id} = result
|
||||
assert to_string(activity.id) == id
|
||||
|
||||
assert result["pleroma"]["emoji_reactions"] == [
|
||||
%{
|
||||
"name" => "dinosaur",
|
||||
"count" => 1,
|
||||
"me" => true,
|
||||
"url" => "http://localhost:4001/emoji/dino walking.gif",
|
||||
"account_ids" => [other_user.id]
|
||||
}
|
||||
]
|
||||
|
||||
# Reacting with a remote emoji
|
||||
note =
|
||||
insert(:note,
|
||||
user: user,
|
||||
data: %{
|
||||
"reactions" => [
|
||||
["👍", [other_user.ap_id], nil],
|
||||
["wow", [other_user.ap_id], "https://remote/emoji/wow"]
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
activity = insert(:note_activity, note: note, user: user)
|
||||
|
||||
result =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> assign(:token, insert(:oauth_token, user: user, scopes: ["write:statuses"]))
|
||||
|> put("/api/v1/pleroma/statuses/#{activity.id}/reactions/:wow@remote:")
|
||||
|> json_response(200)
|
||||
|
||||
assert result["pleroma"]["emoji_reactions"] == [
|
||||
%{
|
||||
"account_ids" => [other_user.id],
|
||||
"count" => 1,
|
||||
"me" => false,
|
||||
"name" => "👍",
|
||||
"url" => nil
|
||||
},
|
||||
%{
|
||||
"name" => "wow@remote",
|
||||
"count" => 2,
|
||||
"me" => true,
|
||||
"url" => "https://remote/emoji/wow",
|
||||
"account_ids" => [user.id, other_user.id]
|
||||
}
|
||||
]
|
||||
|
||||
# Reacting with a remote custom emoji that hasn't been reacted with yet
|
||||
note =
|
||||
insert(:note,
|
||||
user: user
|
||||
)
|
||||
|
||||
activity = insert(:note_activity, note: note, user: user)
|
||||
|
||||
assert conn
|
||||
|> assign(:user, user)
|
||||
|> assign(:token, insert(:oauth_token, user: user, scopes: ["write:statuses"]))
|
||||
|> put("/api/v1/pleroma/statuses/#{activity.id}/reactions/:wow@remote:")
|
||||
|> json_response(400)
|
||||
|
||||
# Reacting with a non-emoji
|
||||
assert conn
|
||||
|> assign(:user, other_user)
|
||||
|
@ -46,8 +136,21 @@ test "DELETE /api/v1/pleroma/statuses/:id/reactions/:emoji", %{conn: conn} do
|
|||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "#cofe"})
|
||||
note =
|
||||
insert(:note,
|
||||
user: user,
|
||||
data: %{"reactions" => [["wow", [user.ap_id], "https://remote/emoji/wow"]]}
|
||||
)
|
||||
|
||||
activity = insert(:note_activity, note: note, user: user)
|
||||
|
||||
ObanHelpers.perform_all()
|
||||
|
||||
{:ok, _reaction_activity} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
|
||||
{:ok, _reaction_activity} = CommonAPI.react_with_emoji(activity.id, other_user, ":dinosaur:")
|
||||
|
||||
{:ok, _reaction_activity} =
|
||||
CommonAPI.react_with_emoji(activity.id, other_user, ":wow@remote:")
|
||||
|
||||
ObanHelpers.perform_all()
|
||||
|
||||
|
@ -60,11 +163,47 @@ test "DELETE /api/v1/pleroma/statuses/:id/reactions/:emoji", %{conn: conn} do
|
|||
assert %{"id" => id} = json_response_and_validate_schema(result, 200)
|
||||
assert to_string(activity.id) == id
|
||||
|
||||
# Remove custom emoji
|
||||
|
||||
result =
|
||||
conn
|
||||
|> assign(:user, other_user)
|
||||
|> assign(:token, insert(:oauth_token, user: other_user, scopes: ["write:statuses"]))
|
||||
|> delete("/api/v1/pleroma/statuses/#{activity.id}/reactions/:dinosaur:")
|
||||
|
||||
assert %{"id" => id} = json_response_and_validate_schema(result, 200)
|
||||
assert to_string(activity.id) == id
|
||||
|
||||
ObanHelpers.perform_all()
|
||||
|
||||
object = Object.get_by_ap_id(activity.data["object"])
|
||||
|
||||
assert object.data["reaction_count"] == 0
|
||||
assert object.data["reaction_count"] == 2
|
||||
|
||||
# Remove custom remote emoji
|
||||
result =
|
||||
conn
|
||||
|> assign(:user, other_user)
|
||||
|> assign(:token, insert(:oauth_token, user: other_user, scopes: ["write:statuses"]))
|
||||
|> delete("/api/v1/pleroma/statuses/#{activity.id}/reactions/:wow@remote:")
|
||||
|> json_response(200)
|
||||
|
||||
assert result["pleroma"]["emoji_reactions"] == [
|
||||
%{
|
||||
"name" => "wow@remote",
|
||||
"count" => 1,
|
||||
"me" => false,
|
||||
"url" => "https://remote/emoji/wow",
|
||||
"account_ids" => [user.id]
|
||||
}
|
||||
]
|
||||
|
||||
# Remove custom remote emoji that hasn't been reacted with yet
|
||||
assert conn
|
||||
|> assign(:user, other_user)
|
||||
|> assign(:token, insert(:oauth_token, user: other_user, scopes: ["write:statuses"]))
|
||||
|> delete("/api/v1/pleroma/statuses/#{activity.id}/reactions/:zoop@remote:")
|
||||
|> json_response(400)
|
||||
end
|
||||
|
||||
test "GET /api/v1/pleroma/statuses/:id/reactions", %{conn: conn} do
|
||||
|
@ -181,7 +320,15 @@ test "GET /api/v1/pleroma/statuses/:id/reactions/:emoji", %{conn: conn} do
|
|||
{:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅")
|
||||
{:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
|
||||
|
||||
assert [%{"name" => "🎅", "count" => 1, "accounts" => [represented_user], "me" => false}] =
|
||||
assert [
|
||||
%{
|
||||
"name" => "🎅",
|
||||
"count" => 1,
|
||||
"accounts" => [represented_user],
|
||||
"me" => false,
|
||||
"url" => nil
|
||||
}
|
||||
] =
|
||||
conn
|
||||
|> get("/api/v1/pleroma/statuses/#{activity.id}/reactions/🎅")
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
|
Loading…
Reference in a new issue