Allow reacting with remote emoji when they exist on the post (#200)
[akkoma] / lib / pleroma / web / activity_pub / builder.ex
index 97ceaf08ecfa8ec008d753948bd0a502903162e4..71ccbdef59a2ddc83acd0693243b06edd5ba9513 100644 (file)
@@ -55,37 +55,84 @@ defmodule Pleroma.Web.ActivityPub.Builder 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
+    data
+    |> Map.put("content", Emoji.maybe_quote(emoji))
+    |> Map.put("type", "EmojiReact")
+    |> Map.put("tag", [
+      %{}
+      |> Map.put("id", url)
+      |> Map.put("type", "Emoji")
+      |> Map.put("name", Emoji.maybe_quote(emoji))
+      |> Map.put(
+        "icon",
+        %{}
+        |> Map.put("type", "Image")
+        |> Map.put("url", url)
+      )
+    ])
+  end
+
+  defp remote_custom_emoji_react(
+         %{data: %{"reactions" => existing_reactions}} = object,
+         data,
+         emoji
+       ) do
+    [emoji_code, instance] = String.split(Emoji.stripped_name(emoji), "@")
+
+    matching_reaction =
+      Enum.find(
+        existing_reactions,
+        fn [name, _, url] ->
+          url = URI.parse(url)
+          url.host == instance && name == emoji_code
+        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 %{} = emojo <- Emoji.get(emoji) do
+      path = emojo |> Map.get(:file)
+      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 =
         if Emoji.is_unicode_emoji?(emoji) do
-          data
-          |> Map.put("content", emoji)
-          |> Map.put("type", "EmojiReact")
+          unicode_emoji_react(object, data, emoji)
         else
-          with %{} = emojo <- Emoji.get(emoji) do
-            path = emojo |> Map.get(:file)
-            url = "#{Endpoint.url()}#{path}"
-
-            data
-            |> Map.put("content", emoji)
-            |> Map.put("type", "EmojiReact")
-            |> Map.put("tag", [
-              %{}
-              |> Map.put("id", url)
-              |> Map.put("type", "Emoji")
-              |> Map.put("name", emojo.code)
-              |> Map.put(
-                "icon",
-                %{}
-                |> Map.put("type", "Image")
-                |> Map.put("url", url)
-              )
-            ])
-          else
-            _ -> {:error, "Emoji does not exist"}
-          end
+          custom_emoji_react(object, data, emoji)
         end
 
       {:ok, data, meta}