From 37c2deb3ddcd473be9a55bbbd48b370b67a0384a Mon Sep 17 00:00:00 2001 From: href Date: Tue, 12 May 2020 21:06:23 +0200 Subject: [PATCH 1/9] Expand and authorize streams in Streamer directly (backport from !2519) --- .../web/mastodon_api/websocket_handler.ex | 69 +++--------- lib/pleroma/web/streamer/state.ex | 19 +--- lib/pleroma/web/streamer/streamer.ex | 65 +++++++++++ test/integration/mastodon_websocket_test.exs | 12 +- test/notification_test.exs | 11 +- test/web/streamer/streamer_test.exs | 104 ++++++++++++++---- 6 files changed, 180 insertions(+), 100 deletions(-) diff --git a/lib/pleroma/web/mastodon_api/websocket_handler.ex b/lib/pleroma/web/mastodon_api/websocket_handler.ex index 5652a37c1..b1aebe014 100644 --- a/lib/pleroma/web/mastodon_api/websocket_handler.ex +++ b/lib/pleroma/web/mastodon_api/websocket_handler.ex @@ -12,29 +12,15 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do @behaviour :cowboy_websocket - @streams [ - "public", - "public:local", - "public:media", - "public:local:media", - "user", - "user:notification", - "direct", - "list", - "hashtag" - ] - @anonymous_streams ["public", "public:local", "hashtag"] - # Handled by periodic keepalive in Pleroma.Web.Streamer.Ping. @timeout :infinity def init(%{qs: qs} = req, state) do - with params <- :cow_qs.parse_qs(qs), + with params <- Enum.into(:cow_qs.parse_qs(qs), %{}), sec_websocket <- :cowboy_req.header("sec-websocket-protocol", req, nil), - access_token <- List.keyfind(params, "access_token", 0), - {_, stream} <- List.keyfind(params, "stream", 0), - {:ok, user} <- allow_request(stream, [access_token, sec_websocket]), - topic when is_binary(topic) <- expand_topic(stream, params) do + access_token <- Map.get(params, "access_token"), + {:ok, user} <- authenticate_request(access_token, sec_websocket), + {:ok, topic} <- Streamer.get_topic(Map.get(params, "stream"), user, params) do req = if sec_websocket do :cowboy_req.set_resp_header("sec-websocket-protocol", sec_websocket, req) @@ -44,14 +30,14 @@ def init(%{qs: qs} = req, state) do {:cowboy_websocket, req, %{user: user, topic: topic}, %{idle_timeout: @timeout}} else - {:error, code} -> - Logger.debug("#{__MODULE__} denied connection: #{inspect(code)} - #{inspect(req)}") - {:ok, req} = :cowboy_req.reply(code, req) + {:error, :bad_topic} -> + Logger.debug("#{__MODULE__} bad topic #{inspect(req)}") + {:ok, req} = :cowboy_req.reply(404, req) {:ok, req, state} - error -> - Logger.debug("#{__MODULE__} denied connection: #{inspect(error)} - #{inspect(req)}") - {:ok, req} = :cowboy_req.reply(400, req) + {:error, :unauthorized} -> + Logger.debug("#{__MODULE__} authentication error: #{inspect(req)}") + {:ok, req} = :cowboy_req.reply(401, req) {:ok, req, state} end end @@ -93,50 +79,23 @@ def terminate(reason, _req, state) do end # Public streams without authentication. - defp allow_request(stream, [nil, nil]) when stream in @anonymous_streams do + defp authenticate_request(nil, nil) do {:ok, nil} end # Authenticated streams. - defp allow_request(stream, [access_token, sec_websocket]) when stream in @streams do - token = - with {"access_token", token} <- access_token do - token - else - _ -> sec_websocket - end + defp authenticate_request(access_token, sec_websocket) do + token = access_token || sec_websocket with true <- is_bitstring(token), %Token{user_id: user_id} <- Repo.get_by(Token, token: token), user = %User{} <- User.get_cached_by_id(user_id) do {:ok, user} else - _ -> {:error, 403} + _ -> {:error, :unauthorized} end end - # Not authenticated. - defp allow_request(stream, _) when stream in @streams, do: {:error, 403} - - # No matching stream. - defp allow_request(_, _), do: {:error, 404} - - defp expand_topic("hashtag", params) do - case List.keyfind(params, "tag", 0) do - {_, tag} -> "hashtag:#{tag}" - _ -> nil - end - end - - defp expand_topic("list", params) do - case List.keyfind(params, "list", 0) do - {_, list} -> "list:#{list}" - _ -> nil - end - end - - defp expand_topic(topic, _), do: topic - defp streamer_socket(state) do %{transport_pid: self(), assigns: state} end diff --git a/lib/pleroma/web/streamer/state.ex b/lib/pleroma/web/streamer/state.ex index 999550b88..4eb462a1a 100644 --- a/lib/pleroma/web/streamer/state.ex +++ b/lib/pleroma/web/streamer/state.ex @@ -36,30 +36,28 @@ def handle_call(:get_state, _from, state) do end def handle_call({:add, topic, socket}, _from, %{sockets: sockets} = state) do - internal_topic = internal_topic(topic, socket) stream_socket = StreamerSocket.from_socket(socket) sockets_for_topic = sockets - |> Map.get(internal_topic, []) + |> Map.get(topic, []) |> List.insert_at(0, stream_socket) |> Enum.uniq() - state = put_in(state, [:sockets, internal_topic], sockets_for_topic) + state = put_in(state, [:sockets, topic], sockets_for_topic) Logger.debug("Got new conn for #{topic}") {:reply, state, state} end def handle_call({:remove, topic, socket}, _from, %{sockets: sockets} = state) do - internal_topic = internal_topic(topic, socket) stream_socket = StreamerSocket.from_socket(socket) sockets_for_topic = sockets - |> Map.get(internal_topic, []) + |> Map.get(topic, []) |> List.delete(stream_socket) - state = Kernel.put_in(state, [:sockets, internal_topic], sockets_for_topic) + state = Kernel.put_in(state, [:sockets, topic], sockets_for_topic) {:reply, state, state} end @@ -70,13 +68,4 @@ defp do_remove_socket(:test, _, _) do defp do_remove_socket(_env, topic, socket) do GenServer.call(__MODULE__, {:remove, topic, socket}) end - - defp internal_topic(topic, socket) - when topic in ~w[user user:notification direct] do - "#{topic}:#{socket.assigns[:user].id}" - end - - defp internal_topic(topic, _) do - topic - end end diff --git a/lib/pleroma/web/streamer/streamer.ex b/lib/pleroma/web/streamer/streamer.ex index 814d5a729..1e5700b6a 100644 --- a/lib/pleroma/web/streamer/streamer.ex +++ b/lib/pleroma/web/streamer/streamer.ex @@ -5,10 +5,75 @@ defmodule Pleroma.Web.Streamer do alias Pleroma.Web.Streamer.State alias Pleroma.Web.Streamer.Worker + alias Pleroma.User @timeout 60_000 @mix_env Mix.env() + @public_streams ["public", "public:local", "public:media", "public:local:media"] + @user_streams ["user", "user:notification", "direct"] + + @doc "Expands and authorizes a stream, and registers the process for streaming." + @spec get_topic_and_add_socket(stream :: String.t(), State.t(), Map.t() | nil) :: + {:ok, topic :: String.t()} | {:error, :bad_topic} | {:error, :unauthorized} + def get_topic_and_add_socket(stream, socket, params \\ %{}) do + user = + case socket do + %{assigns: %{user: user}} -> user + _ -> nil + end + + case get_topic(stream, user, params) do + {:ok, topic} -> + add_socket(topic, socket) + {:ok, topic} + + error -> + error + end + end + + @doc "Expand and authorizes a stream" + @spec get_topic(stream :: String.t(), User.t() | nil, Map.t()) :: + {:ok, topic :: String.t()} | {:error, :bad_topic} + def get_topic(stream, user, params \\ %{}) + + # Allow all public steams. + def get_topic(stream, _, _) when stream in @public_streams do + {:ok, stream} + end + + # Allow all hashtags streams. + def get_topic("hashtag", _, %{"tag" => tag}) do + {:ok, "hashtag:" <> tag} + end + + # Expand user streams. + def get_topic(stream, %User{} = user, _) when stream in @user_streams do + {:ok, stream <> ":" <> to_string(user.id)} + end + + def get_topic(stream, _, _) when stream in @user_streams do + {:error, :unauthorized} + end + + # List streams. + def get_topic("list", %User{} = user, %{"list" => id}) do + if Pleroma.List.get(id, user) do + {:ok, "list:" <> to_string(id)} + else + {:error, :bad_topic} + end + end + + def get_topic("list", _, _) do + {:error, :unauthorized} + end + + def get_topic(_, _, _) do + {:error, :bad_topic} + end + def add_socket(topic, socket) do State.add_socket(topic, socket) end diff --git a/test/integration/mastodon_websocket_test.exs b/test/integration/mastodon_websocket_test.exs index bd229c55f..39be5ad08 100644 --- a/test/integration/mastodon_websocket_test.exs +++ b/test/integration/mastodon_websocket_test.exs @@ -35,7 +35,7 @@ def start_socket(qs \\ nil, headers \\ []) do test "refuses invalid requests" do capture_log(fn -> - assert {:error, {400, _}} = start_socket() + assert {:error, {404, _}} = start_socket() assert {:error, {404, _}} = start_socket("?stream=ncjdk") Process.sleep(30) end) @@ -43,8 +43,8 @@ test "refuses invalid requests" do test "requires authentication and a valid token for protected streams" do capture_log(fn -> - assert {:error, {403, _}} = start_socket("?stream=user&access_token=aaaaaaaaaaaa") - assert {:error, {403, _}} = start_socket("?stream=user") + assert {:error, {401, _}} = start_socket("?stream=user&access_token=aaaaaaaaaaaa") + assert {:error, {401, _}} = start_socket("?stream=user") Process.sleep(30) end) end @@ -103,7 +103,7 @@ test "accepts the 'user' stream", %{token: token} = _state do assert {:ok, _} = start_socket("?stream=user&access_token=#{token.token}") assert capture_log(fn -> - assert {:error, {403, "Forbidden"}} = start_socket("?stream=user") + assert {:error, {401, _}} = start_socket("?stream=user") Process.sleep(30) end) =~ ":badarg" end @@ -112,7 +112,7 @@ test "accepts the 'user:notification' stream", %{token: token} = _state do assert {:ok, _} = start_socket("?stream=user:notification&access_token=#{token.token}") assert capture_log(fn -> - assert {:error, {403, "Forbidden"}} = start_socket("?stream=user:notification") + assert {:error, {401, _}} = start_socket("?stream=user:notification") Process.sleep(30) end) =~ ":badarg" end @@ -121,7 +121,7 @@ test "accepts valid token on Sec-WebSocket-Protocol header", %{token: token} do assert {:ok, _} = start_socket("?stream=user", [{"Sec-WebSocket-Protocol", token.token}]) assert capture_log(fn -> - assert {:error, {403, "Forbidden"}} = + assert {:error, {401, _}} = start_socket("?stream=user", [{"Sec-WebSocket-Protocol", "I am a friend"}]) Process.sleep(30) diff --git a/test/notification_test.exs b/test/notification_test.exs index e12418db3..d04754a9d 100644 --- a/test/notification_test.exs +++ b/test/notification_test.exs @@ -164,12 +164,13 @@ test "it creates a notification for user and send to the 'user' and the 'user:no user = insert(:user) task = Task.async(fn -> assert_receive {:text, _}, 4_000 end) task_user_notification = Task.async(fn -> assert_receive {:text, _}, 4_000 end) - Streamer.add_socket("user", %{transport_pid: task.pid, assigns: %{user: user}}) - Streamer.add_socket( - "user:notification", - %{transport_pid: task_user_notification.pid, assigns: %{user: user}} - ) + Streamer.get_topic_and_add_socket("user", %{transport_pid: task.pid, assigns: %{user: user}}) + + Streamer.get_topic_and_add_socket("user:notification", %{ + transport_pid: task_user_notification.pid, + assigns: %{user: user} + }) activity = insert(:note_activity) diff --git a/test/web/streamer/streamer_test.exs b/test/web/streamer/streamer_test.exs index 720f8fa44..cbd5ec462 100644 --- a/test/web/streamer/streamer_test.exs +++ b/test/web/streamer/streamer_test.exs @@ -17,11 +17,81 @@ defmodule Pleroma.Web.StreamerTest do @moduletag needs_streamer: true, capture_log: true - @streamer_timeout 150 + @streamer_timeout 300 @streamer_start_wait 10 clear_config([:instance, :skip_thread_containment]) + describe "get_topic without an user" do + test "allows public" do + assert {:ok, "public"} = Streamer.get_topic("public", nil) + assert {:ok, "public:local"} = Streamer.get_topic("public:local", nil) + assert {:ok, "public:media"} = Streamer.get_topic("public:media", nil) + assert {:ok, "public:local:media"} = Streamer.get_topic("public:local:media", nil) + end + + test "allows hashtag streams" do + assert {:ok, "hashtag:cofe"} = Streamer.get_topic("hashtag", nil, %{"tag" => "cofe"}) + end + + test "disallows user streams" do + assert {:error, _} = Streamer.get_topic("user", nil) + assert {:error, _} = Streamer.get_topic("user:notification", nil) + assert {:error, _} = Streamer.get_topic("direct", nil) + end + + test "disallows list streams" do + assert {:error, _} = Streamer.get_topic("list", nil, %{"list" => 42}) + end + end + + describe "get_topic with an user" do + setup do + user = insert(:user) + {:ok, %{user: user}} + end + + test "allows public streams", %{user: user} do + assert {:ok, "public"} = Streamer.get_topic("public", user) + assert {:ok, "public:local"} = Streamer.get_topic("public:local", user) + assert {:ok, "public:media"} = Streamer.get_topic("public:media", user) + assert {:ok, "public:local:media"} = Streamer.get_topic("public:local:media", user) + end + + test "allows user streams", %{user: user} do + expected_user_topic = "user:#{user.id}" + expected_notif_topic = "user:notification:#{user.id}" + expected_direct_topic = "direct:#{user.id}" + assert {:ok, ^expected_user_topic} = Streamer.get_topic("user", user) + assert {:ok, ^expected_notif_topic} = Streamer.get_topic("user:notification", user) + assert {:ok, ^expected_direct_topic} = Streamer.get_topic("direct", user) + end + + test "allows hashtag streams", %{user: user} do + assert {:ok, "hashtag:cofe"} = Streamer.get_topic("hashtag", user, %{"tag" => "cofe"}) + end + + test "disallows registering to an user stream", %{user: user} do + another_user = insert(:user) + assert {:error, _} = Streamer.get_topic("user:#{another_user.id}", user) + assert {:error, _} = Streamer.get_topic("user:notification:#{another_user.id}", user) + assert {:error, _} = Streamer.get_topic("direct:#{another_user.id}", user) + end + + test "allows list stream that are owned by the user", %{user: user} do + {:ok, list} = List.create("Test", user) + assert {:error, _} = Streamer.get_topic("list:#{list.id}", user) + assert {:ok, _} = Streamer.get_topic("list", user, %{"list" => list.id}) + end + + test "disallows list stream that are not owned by the user", %{user: user} do + another_user = insert(:user) + {:ok, list} = List.create("Test", another_user) + assert {:error, _} = Streamer.get_topic("list:#{list.id}", user) + assert {:error, _} = Streamer.get_topic("list", user, %{"list" => list.id}) + end + end + describe "user streams" do setup do user = insert(:user) @@ -35,7 +105,7 @@ test "it sends notify to in the 'user' stream", %{user: user, notify: notify} do assert_receive {:text, _}, @streamer_timeout end) - Streamer.add_socket( + Streamer.get_topic_and_add_socket( "user", %{transport_pid: task.pid, assigns: %{user: user}} ) @@ -50,7 +120,7 @@ test "it sends notify to in the 'user:notification' stream", %{user: user, notif assert_receive {:text, _}, @streamer_timeout end) - Streamer.add_socket( + Streamer.get_topic_and_add_socket( "user:notification", %{transport_pid: task.pid, assigns: %{user: user}} ) @@ -70,7 +140,7 @@ test "it doesn't send notify to the 'user:notification' stream when a user is bl task = Task.async(fn -> refute_receive {:text, _}, @streamer_timeout end) - Streamer.add_socket( + Streamer.get_topic_and_add_socket( "user:notification", %{transport_pid: task.pid, assigns: %{user: user}} ) @@ -90,7 +160,7 @@ test "it doesn't send notify to the 'user:notification' stream when a thread is task = Task.async(fn -> refute_receive {:text, _}, @streamer_timeout end) - Streamer.add_socket( + Streamer.get_topic_and_add_socket( "user:notification", %{transport_pid: task.pid, assigns: %{user: user}} ) @@ -110,7 +180,7 @@ test "it doesn't send notify to the 'user:notification' stream' when a domain is task = Task.async(fn -> refute_receive {:text, _}, @streamer_timeout end) - Streamer.add_socket( + Streamer.get_topic_and_add_socket( "user:notification", %{transport_pid: task.pid, assigns: %{user: user}} ) @@ -127,7 +197,7 @@ test "it sends follow activities to the 'user:notification' stream", %{ Process.sleep(@streamer_start_wait) - Streamer.add_socket( + Streamer.get_topic_and_add_socket( "user:notification", %{transport_pid: task.pid, assigns: %{user: user}} ) @@ -415,14 +485,10 @@ test "it sends wanted private posts to list" do assert_receive {:text, _}, 1_000 end) - fake_socket = %StreamerSocket{ - transport_pid: task.pid, - user: user_a - } - - Streamer.add_socket( - "list:#{list.id}", - fake_socket + Streamer.get_topic_and_add_socket( + "list", + %{transport_pid: task.pid, assigns: %{user: user_a}}, + %{"list" => list.id} ) Worker.handle_call({:stream, "list", activity}, self(), %{}) @@ -497,7 +563,7 @@ test "it doesn't send posts from muted threads" do task = Task.async(fn -> refute_receive {:text, _}, @streamer_timeout end) - Streamer.add_socket( + Streamer.get_topic_and_add_socket( "user", %{transport_pid: task.pid, assigns: %{user: user2}} ) @@ -527,7 +593,7 @@ test "it sends conversation update to the 'direct' stream", %{} do assert last_status["pleroma"]["direct_conversation_id"] == participation.id end) - Streamer.add_socket( + Streamer.get_topic_and_add_socket( "direct", %{transport_pid: task.pid, assigns: %{user: user}} ) @@ -561,7 +627,7 @@ test "it doesn't send conversation update to the 'direct' stream when the last m Process.sleep(@streamer_start_wait) - Streamer.add_socket( + Streamer.get_topic_and_add_socket( "direct", %{transport_pid: task.pid, assigns: %{user: user}} ) @@ -604,7 +670,7 @@ test "it sends conversation update to the 'direct' stream when a message is dele Process.sleep(@streamer_start_wait) - Streamer.add_socket( + Streamer.get_topic_and_add_socket( "direct", %{transport_pid: task.pid, assigns: %{user: user}} ) From 40d0775c7f0ed8c52a513146bf04df3f783a8eb1 Mon Sep 17 00:00:00 2001 From: href Date: Tue, 12 May 2020 21:27:54 +0200 Subject: [PATCH 2/9] Reorder alias --- lib/pleroma/web/streamer/streamer.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/web/streamer/streamer.ex b/lib/pleroma/web/streamer/streamer.ex index 1e5700b6a..b7294d084 100644 --- a/lib/pleroma/web/streamer/streamer.ex +++ b/lib/pleroma/web/streamer/streamer.ex @@ -3,9 +3,9 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Streamer do + alias Pleroma.User alias Pleroma.Web.Streamer.State alias Pleroma.Web.Streamer.Worker - alias Pleroma.User @timeout 60_000 @mix_env Mix.env() From b5b675fa14094ae13b78b3de137a9c6de83b3696 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Tue, 12 May 2020 23:03:21 +0300 Subject: [PATCH 3/9] fix eldap being required for non-OTP releases --- mix.exs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mix.exs b/mix.exs index 9f9679f62..6e5d01e51 100644 --- a/mix.exs +++ b/mix.exs @@ -36,7 +36,7 @@ def project do releases: [ pleroma: [ include_executables_for: [:unix], - applications: [ex_syslogger: :load, syslog: :load], + applications: [ex_syslogger: :load, syslog: :load, eldap: :transient], steps: [:assemble, ©_files/1, ©_nginx_config/1] ] ] @@ -69,8 +69,7 @@ def application do :comeonin, :quack, :fast_sanitize, - :ssl, - :eldap + :ssl ], included_applications: [:ex_syslogger] ] From ec5e05780292710a3454b21d32a1af053cb603e0 Mon Sep 17 00:00:00 2001 From: lain Date: Tue, 12 May 2020 12:29:37 +0200 Subject: [PATCH 4/9] Transmogrifier: On incoming follow accept, update follow counts. --- lib/pleroma/web/activity_pub/transmogrifier.ex | 6 +++++- test/web/activity_pub/transmogrifier_test.exs | 6 ++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 831739c5f..3fc4762d6 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -544,6 +544,9 @@ def handle_incoming( {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"), %User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]), {:ok, _relationship} <- FollowingRelationship.update(follower, followed, :follow_accept) do + User.update_follower_count(followed) + User.update_following_count(follower) + ActivityPub.accept(%{ to: follow_activity.data["to"], type: "Accept", @@ -553,7 +556,8 @@ def handle_incoming( activity_id: id }) else - _e -> :error + _e -> + :error end end diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs index 2a3fd92b4..601e5f966 100644 --- a/test/web/activity_pub/transmogrifier_test.exs +++ b/test/web/activity_pub/transmogrifier_test.exs @@ -1120,6 +1120,12 @@ test "it works for incoming accepts which are referenced by IRI only" do follower = User.get_cached_by_id(follower.id) assert User.following?(follower, followed) == true + + follower = User.get_by_id(follower.id) + assert follower.following_count == 1 + + followed = User.get_by_id(followed.id) + assert followed.follower_count == 1 end test "it fails for incoming accepts which cannot be correlated" do From a8abf1ada6d00448533917c8d51cf5907ccd94d8 Mon Sep 17 00:00:00 2001 From: lain Date: Tue, 12 May 2020 10:52:46 +0200 Subject: [PATCH 5/9] ActivityPub: Fix non-federating blocks. --- lib/pleroma/web/activity_pub/activity_pub.ex | 4 +-- lib/pleroma/web/activity_pub/utils.ex | 8 +++-- test/web/activity_pub/activity_pub_test.exs | 37 +++++++++++++++++--- 3 files changed, 40 insertions(+), 9 deletions(-) diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 5f895406d..c4f83f9e1 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -604,7 +604,6 @@ def block(blocker, blocked, activity_id \\ nil, local \\ true) do end defp do_block(blocker, blocked, activity_id, local) do - outgoing_blocks = Config.get([:activitypub, :outgoing_blocks]) unfollow_blocked = Config.get([:activitypub, :unfollow_blocked]) if unfollow_blocked do @@ -612,8 +611,7 @@ defp do_block(blocker, blocked, activity_id, local) do if follow_activity, do: unfollow(blocker, blocked, nil, local) end - with true <- outgoing_blocks, - block_data <- make_block_data(blocker, blocked, activity_id), + with block_data <- make_block_data(blocker, blocked, activity_id), {:ok, activity} <- insert(block_data, local), :ok <- maybe_federate(activity) do {:ok, activity} diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 15dd2ed45..a49cfa35e 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -6,6 +6,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do alias Ecto.Changeset alias Ecto.UUID alias Pleroma.Activity + alias Pleroma.Config alias Pleroma.Notification alias Pleroma.Object alias Pleroma.Repo @@ -169,8 +170,11 @@ def create_context(context) do Enqueues an activity for federation if it's local """ @spec maybe_federate(any()) :: :ok - def maybe_federate(%Activity{local: true} = activity) do - if Pleroma.Config.get!([:instance, :federating]) do + def maybe_federate(%Activity{local: true, data: %{"type" => type}} = activity) do + outgoing_blocks = Config.get([:activitypub, :outgoing_blocks]) + + with true <- Config.get!([:instance, :federating]), + true <- type != "Block" || outgoing_blocks do Pleroma.Web.Federator.publish(activity) end diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs index 433859dab..e6c4299ba 100644 --- a/test/web/activity_pub/activity_pub_test.exs +++ b/test/web/activity_pub/activity_pub_test.exs @@ -1351,15 +1351,44 @@ test "reverts block activity on error" do assert Repo.aggregate(Object, :count, :id) == 0 end + clear_config([:instance, :federating]) + test "creates a block activity" do + Config.put([:instance, :federating], true) blocker = insert(:user) blocked = insert(:user) - {:ok, activity} = ActivityPub.block(blocker, blocked) + with_mock Pleroma.Web.Federator, + publish: fn _ -> nil end do + {:ok, activity} = ActivityPub.block(blocker, blocked) - assert activity.data["type"] == "Block" - assert activity.data["actor"] == blocker.ap_id - assert activity.data["object"] == blocked.ap_id + assert activity.data["type"] == "Block" + assert activity.data["actor"] == blocker.ap_id + assert activity.data["object"] == blocked.ap_id + + assert called(Pleroma.Web.Federator.publish(activity)) + end + end + + clear_config([:instance, :federating]) + clear_config([:activitypub, :outgoing_blocks]) + + test "works with outgoing blocks disabled, but doesn't federate" do + Config.put([:instance, :federating], true) + Config.put([:activitypub, :outgoing_blocks], false) + blocker = insert(:user) + blocked = insert(:user) + + with_mock Pleroma.Web.Federator, + publish: fn _ -> nil end do + {:ok, activity} = ActivityPub.block(blocker, blocked) + + assert activity.data["type"] == "Block" + assert activity.data["actor"] == blocker.ap_id + assert activity.data["object"] == blocked.ap_id + + refute called(Pleroma.Web.Federator.publish(:_)) + end end test "reverts unblock activity on error" do From 3a63f84c45e057dbfdcedba356478f32349c737a Mon Sep 17 00:00:00 2001 From: rinpatch Date: Tue, 12 May 2020 23:09:44 +0300 Subject: [PATCH 6/9] pleroma-fe bundle: update to 5d49edc823ba2ea3e34d4fd6c5efcc84ef9712f7 --- priv/static/index.html | 2 +- .../static/font/fontello.1588431888583.woff | Bin 13836 -> 0 bytes .../static/font/fontello.1588431888583.woff2 | Bin 11668 -> 0 bytes ...1888583.eot => fontello.1589314090288.eot} | Bin 22752 -> 22976 bytes ...1888583.svg => fontello.1589314090288.svg} | 2 + ...1888583.ttf => fontello.1589314090288.ttf} | Bin 22584 -> 22808 bytes .../static/font/fontello.1589314090288.woff | Bin 0 -> 13988 bytes .../static/font/fontello.1589314090288.woff2 | Bin 0 -> 11816 bytes ...1888583.css => fontello.1589314090288.css} | 14 +- priv/static/static/fontello.json | 6 + ...3f92c77a1.js => 2.f9a5c4aba770b3f9f9e0.js} | 4 +- ...1.js.map => 2.f9a5c4aba770b3f9f9e0.js.map} | 2 +- .../static/js/app.57951e6e5e198d1a1266.js | 2 - .../static/js/app.57951e6e5e198d1a1266.js.map | 1 - .../static/js/app.82334f8362acc4bbcb6f.js | 2 + .../static/js/app.82334f8362acc4bbcb6f.js.map | 1 + ...js => vendors~app.a516afd698489b59a809.js} | 14 +- .../vendors~app.a516afd698489b59a809.js.map | 1 + .../vendors~app.c67e1a363ece7f1f7152.js.map | 1 - priv/static/static/static-fe.css | 176 ------------------ priv/static/sw-pleroma.js | 2 +- 21 files changed, 32 insertions(+), 198 deletions(-) delete mode 100644 priv/static/static/font/fontello.1588431888583.woff delete mode 100644 priv/static/static/font/fontello.1588431888583.woff2 rename priv/static/static/font/{fontello.1588431888583.eot => fontello.1589314090288.eot} (91%) rename priv/static/static/font/{fontello.1588431888583.svg => fontello.1589314090288.svg} (98%) rename priv/static/static/font/{fontello.1588431888583.ttf => fontello.1589314090288.ttf} (91%) create mode 100644 priv/static/static/font/fontello.1589314090288.woff create mode 100644 priv/static/static/font/fontello.1589314090288.woff2 rename priv/static/static/{fontello.1588431888583.css => fontello.1589314090288.css} (88%) rename priv/static/static/js/{2.93c984e8c993f92c77a1.js => 2.f9a5c4aba770b3f9f9e0.js} (79%) rename priv/static/static/js/{2.93c984e8c993f92c77a1.js.map => 2.f9a5c4aba770b3f9f9e0.js.map} (98%) delete mode 100644 priv/static/static/js/app.57951e6e5e198d1a1266.js delete mode 100644 priv/static/static/js/app.57951e6e5e198d1a1266.js.map create mode 100644 priv/static/static/js/app.82334f8362acc4bbcb6f.js create mode 100644 priv/static/static/js/app.82334f8362acc4bbcb6f.js.map rename priv/static/static/js/{vendors~app.c67e1a363ece7f1f7152.js => vendors~app.a516afd698489b59a809.js} (95%) create mode 100644 priv/static/static/js/vendors~app.a516afd698489b59a809.js.map delete mode 100644 priv/static/static/js/vendors~app.c67e1a363ece7f1f7152.js.map delete mode 100644 priv/static/static/static-fe.css diff --git a/priv/static/index.html b/priv/static/index.html index 2958cda1b..e6ba16633 100644 --- a/priv/static/index.html +++ b/priv/static/index.html @@ -1 +1 @@ -Pleroma
\ No newline at end of file +Pleroma
\ No newline at end of file diff --git a/priv/static/static/font/fontello.1588431888583.woff b/priv/static/static/font/fontello.1588431888583.woff deleted file mode 100644 index a50623bbb5e23b49ef2edf9d8178ffab8e9b800f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13836 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 adN{Y<|J>^Vp>Qot4UGdp^rZL;0Q?^cG;vt~ diff --git a/priv/static/static/font/fontello.1588431888583.woff2 b/priv/static/static/font/fontello.1588431888583.woff2 deleted file mode 100644 index 150c93c1ae4deebfaa348528f4c76660a2d0b133..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11668 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 WzuyrL<1DMA?{B6L9l!-NT~LgV|Tdlq{Gpig>UZ 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 diff --git a/priv/static/static/font/fontello.1588431888583.eot b/priv/static/static/font/fontello.1589314090288.eot similarity index 91% rename from priv/static/static/font/fontello.1588431888583.eot rename to priv/static/static/font/fontello.1589314090288.eot index 1f2934b934f32b5b8169b6f92453ae2b9e4cb1fd..538cb5c0a81ccd4a2ffda5e4e43bb501e4d22ccb 100644 GIT binary patch delta 711 zcmZuvUr3Wt6#w1({Zltn(sY7sbE&iJL3C|Rqcn1(snLT-)hwy&|Zk8Oz3gWy~G z(Cn)og1`t;q?h~<40_n>AR&6Ii1v^ry_mxwB3fr!Z{5S;oZs(v&pqdIZ{a!1nM}@& z1Ge!+*(>^Y9P5ky3$rr-y98itBBAQ9>^J%duK-f*#8k#9mwxX7BP7Ea(W8m^#Vj2# z;sh*uR88x|*8;;oC{{${GZEX1Cm#WM12}tJ3#*e&BP(mb;3v|(8WBofJOTuo3AbvA z%=GIoW2Lu5^b@fiPfn=sbNSCeu!HbaLY>yxo$9BAmk2v=s)_LCqedIy9KbjAWIFTg zdbbILvVGXN@Ixl|3|mv8a0#jHPS&jBz0dQ4o_5 zBq0e2Qc`B%NP;t-1Y;2fdvlZRaBa0k64A!m6sf+GbvM+@u6Cct=a!GU+CAQ$?nYnx zNzWN(XR_PnRvPM+MlW9uY1+LQ>t*?#z>ua51$y#Kdl2EV3qi_=W%k(JSnPsgo$R8d zYtpKOG#<~!8KsAag!$%NkvFe9cKp|BPJf3**e+lHU14qRUo;AXKGtj40w378Q4HLw ncG!N9=s zE4i#hf$jglFANMm3P5>w0rFKca!V@C_J#ew2^0Vs^du)g zIdRX_Q^pJoZXrN^Np50A0b?KUG9dp1kgt%Jn45Zbt~k&#w-*cyOlJ%7i%V9X|E0yi z;JE|Hk1HrjEqLmvy@!Dze8c1gjM0p5cvKDjk=K0J#jGP-l z)&POU<}EDUOyW-&7(N0qgE<4kCld%}uwY==oWR~`y?LqgdnOq-`|$7a{5D@1xS4?- zVqmy+*=7oip1i@$oY8Xf3%9$IPr6rd_Q;!q9dEhW&x4y8C=umdwE3L(X2#9!zLq=y D(lUla diff --git a/priv/static/static/font/fontello.1588431888583.svg b/priv/static/static/font/fontello.1589314090288.svg similarity index 98% rename from priv/static/static/font/fontello.1588431888583.svg rename to priv/static/static/font/fontello.1589314090288.svg index 71f81f435..e63fb7529 100644 --- a/priv/static/static/font/fontello.1588431888583.svg +++ b/priv/static/static/font/fontello.1589314090288.svg @@ -114,6 +114,8 @@ + + diff --git a/priv/static/static/font/fontello.1588431888583.ttf b/priv/static/static/font/fontello.1589314090288.ttf similarity index 91% rename from priv/static/static/font/fontello.1588431888583.ttf rename to priv/static/static/font/fontello.1589314090288.ttf index b4fb51df02af787125303094a2f96dd4062c3edd..8ea898a13762cbccc695beb3997b44e7fb763398 100644 GIT binary patch delta 720 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 aU~aAdrPl>i`dW+v0uu<%mGl7!=zjoDqP!RY literal 0 HcmV?d00001 diff --git a/priv/static/static/font/fontello.1589314090288.woff2 b/priv/static/static/font/fontello.1589314090288.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..38cb1616a9be8e9e7616a37a8aed78a454e95281 GIT binary patch literal 11816 zcmV+@F4xg_Pew8T0RR9104^v14*&oF09hCS04>b`0RR9100000000000000000000 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 literal 0 HcmV?d00001 diff --git a/priv/static/static/fontello.1588431888583.css b/priv/static/static/fontello.1589314090288.css similarity index 88% rename from priv/static/static/fontello.1588431888583.css rename to priv/static/static/fontello.1589314090288.css index cb9ad1025..528b8cf1c 100644 --- a/priv/static/static/fontello.1588431888583.css +++ b/priv/static/static/fontello.1589314090288.css @@ -1,11 +1,11 @@ @font-face { font-family: "Icons"; - src: url("./font/fontello.1588431888583.eot"); - src: url("./font/fontello.1588431888583.eot") format("embedded-opentype"), - url("./font/fontello.1588431888583.woff2") format("woff2"), - url("./font/fontello.1588431888583.woff") format("woff"), - url("./font/fontello.1588431888583.ttf") format("truetype"), - url("./font/fontello.1588431888583.svg") format("svg"); + src: url("./font/fontello.1589314090288.eot"); + src: url("./font/fontello.1589314090288.eot") format("embedded-opentype"), + url("./font/fontello.1589314090288.woff2") format("woff2"), + url("./font/fontello.1589314090288.woff") format("woff"), + url("./font/fontello.1589314090288.ttf") format("truetype"), + url("./font/fontello.1589314090288.svg") format("svg"); font-weight: normal; font-style: normal; } @@ -137,6 +137,8 @@ .icon-arrow-curved::before { content: "\e822"; } .icon-link::before { content: "\e823"; } +.icon-share::before { content: "\f1e0"; } + .icon-user::before { content: "\e824"; } .icon-ok::before { content: "\e827"; } diff --git a/priv/static/static/fontello.json b/priv/static/static/fontello.json index 5963b68b4..7f0e7cdd5 100755 --- a/priv/static/static/fontello.json +++ b/priv/static/static/fontello.json @@ -346,6 +346,12 @@ "code": 59427, "src": "fontawesome" }, + { + "uid": "4aad6bb50b02c18508aae9cbe14e784e", + "css": "share", + "code": 61920, + "src": "fontawesome" + }, { "uid": "8b80d36d4ef43889db10bc1f0dc9a862", "css": "user", diff --git a/priv/static/static/js/2.93c984e8c993f92c77a1.js b/priv/static/static/js/2.f9a5c4aba770b3f9f9e0.js similarity index 79% rename from priv/static/static/js/2.93c984e8c993f92c77a1.js rename to priv/static/static/js/2.f9a5c4aba770b3f9f9e0.js index 4faa3d66f..f366644a2 100644 --- a/priv/static/static/js/2.93c984e8c993f92c77a1.js +++ b/priv/static/static/js/2.f9a5c4aba770b3f9f9e0.js @@ -1,2 +1,2 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[2],{580:function(t,e,i){var c=i(581);"string"==typeof c&&(c=[[t.i,c,""]]),c.locals&&(t.exports=c.locals);(0,i(4).default)("cc6cdea4",c,!0,{})},581:function(t,e,i){(t.exports=i(3)(!1)).push([t.i,".sticker-picker{width:100%}.sticker-picker .contents{min-height:250px}.sticker-picker .contents .sticker-picker-content{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:0 4px}.sticker-picker .contents .sticker-picker-content .sticker{display:-ms-flexbox;display:flex;-ms-flex:1 1 auto;flex:1 1 auto;margin:4px;width:56px;height:56px}.sticker-picker .contents .sticker-picker-content .sticker img{height:100%}.sticker-picker .contents .sticker-picker-content .sticker img:hover{filter:drop-shadow(0 0 5px var(--accent,#d8a070))}",""])},582:function(t,e,i){"use strict";i.r(e);var c=i(90),a={components:{TabSwitcher:i(52).a},data:function(){return{meta:{stickers:[]},path:""}},computed:{pack:function(){return this.$store.state.instance.stickers||[]}},methods:{clear:function(){this.meta={stickers:[]}},pick:function(t,e){var i=this,a=this.$store;fetch(t).then(function(t){t.blob().then(function(t){var n=new File([t],e,{mimetype:"image/png"}),s=new FormData;s.append("file",n),c.a.uploadMedia({store:a,formData:s}).then(function(t){i.$emit("uploaded",t),i.clear()},function(t){console.warn("Can't attach sticker"),console.warn(t),i.$emit("upload-failed","default")})})})}}},n=i(0);var s=function(t){i(580)},r=Object(n.a)(a,function(){var t=this,e=t.$createElement,i=t._self._c||e;return i("div",{staticClass:"sticker-picker"},[i("tab-switcher",{staticClass:"tab-switcher",attrs:{"render-only-focused":!0,"scrollable-tabs":""}},t._l(t.pack,function(e){return i("div",{key:e.path,staticClass:"sticker-picker-content",attrs:{"image-tooltip":e.meta.title,image:e.path+e.meta.tabIcon}},t._l(e.meta.stickers,function(c){return i("div",{key:c,staticClass:"sticker",on:{click:function(i){i.stopPropagation(),i.preventDefault(),t.pick(e.path+c,e.meta.title)}}},[i("img",{attrs:{src:e.path+c}})])}),0)}),0)],1)},[],!1,s,null,null);e.default=r.exports}}]); -//# sourceMappingURL=2.93c984e8c993f92c77a1.js.map \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[2],{582:function(t,e,i){var c=i(583);"string"==typeof c&&(c=[[t.i,c,""]]),c.locals&&(t.exports=c.locals);(0,i(3).default)("cc6cdea4",c,!0,{})},583:function(t,e,i){(t.exports=i(2)(!1)).push([t.i,".sticker-picker{width:100%}.sticker-picker .contents{min-height:250px}.sticker-picker .contents .sticker-picker-content{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:0 4px}.sticker-picker .contents .sticker-picker-content .sticker{display:-ms-flexbox;display:flex;-ms-flex:1 1 auto;flex:1 1 auto;margin:4px;width:56px;height:56px}.sticker-picker .contents .sticker-picker-content .sticker img{height:100%}.sticker-picker .contents .sticker-picker-content .sticker img:hover{filter:drop-shadow(0 0 5px var(--accent,#d8a070))}",""])},584:function(t,e,i){"use strict";i.r(e);var c=i(90),a={components:{TabSwitcher:i(52).a},data:function(){return{meta:{stickers:[]},path:""}},computed:{pack:function(){return this.$store.state.instance.stickers||[]}},methods:{clear:function(){this.meta={stickers:[]}},pick:function(t,e){var i=this,a=this.$store;fetch(t).then(function(t){t.blob().then(function(t){var n=new File([t],e,{mimetype:"image/png"}),s=new FormData;s.append("file",n),c.a.uploadMedia({store:a,formData:s}).then(function(t){i.$emit("uploaded",t),i.clear()},function(t){console.warn("Can't attach sticker"),console.warn(t),i.$emit("upload-failed","default")})})})}}},n=i(0);var s=function(t){i(582)},r=Object(n.a)(a,function(){var t=this,e=t.$createElement,i=t._self._c||e;return i("div",{staticClass:"sticker-picker"},[i("tab-switcher",{staticClass:"tab-switcher",attrs:{"render-only-focused":!0,"scrollable-tabs":""}},t._l(t.pack,function(e){return i("div",{key:e.path,staticClass:"sticker-picker-content",attrs:{"image-tooltip":e.meta.title,image:e.path+e.meta.tabIcon}},t._l(e.meta.stickers,function(c){return i("div",{key:c,staticClass:"sticker",on:{click:function(i){i.stopPropagation(),i.preventDefault(),t.pick(e.path+c,e.meta.title)}}},[i("img",{attrs:{src:e.path+c}})])}),0)}),0)],1)},[],!1,s,null,null);e.default=r.exports}}]); +//# sourceMappingURL=2.f9a5c4aba770b3f9f9e0.js.map \ No newline at end of file diff --git a/priv/static/static/js/2.93c984e8c993f92c77a1.js.map b/priv/static/static/js/2.f9a5c4aba770b3f9f9e0.js.map similarity index 98% rename from priv/static/static/js/2.93c984e8c993f92c77a1.js.map rename to priv/static/static/js/2.f9a5c4aba770b3f9f9e0.js.map index 12e4d0c12..76a131851 100644 --- a/priv/static/static/js/2.93c984e8c993f92c77a1.js.map +++ b/priv/static/static/js/2.f9a5c4aba770b3f9f9e0.js.map @@ -1 +1 @@ -{"version":3,"sources":["webpack:///./src/components/sticker_picker/sticker_picker.vue?d6cd","webpack:///./src/components/sticker_picker/sticker_picker.vue?d5ea","webpack:///./src/components/sticker_picker/sticker_picker.js","webpack:///./src/components/sticker_picker/sticker_picker.vue","webpack:///./src/components/sticker_picker/sticker_picker.vue?7504"],"names":["content","__webpack_require__","module","i","locals","exports","add","default","push","StickerPicker","components","TabSwitcher","data","meta","stickers","path","computed","pack","this","$store","state","instance","methods","clear","pick","sticker","name","_this","store","fetch","then","res","blob","file","File","mimetype","formData","FormData","append","statusPosterService","uploadMedia","fileData","$emit","error","console","warn","__vue_styles__","context","Component","Object","component_normalizer","sticker_picker","_vm","_h","$createElement","_c","_self","staticClass","attrs","render-only-focused","scrollable-tabs","_l","stickerpack","key","image-tooltip","title","image","tabIcon","on","click","$event","stopPropagation","preventDefault","src","__webpack_exports__"],"mappings":"6EAGA,IAAAA,EAAcC,EAAQ,KACtB,iBAAAD,MAAA,EAA4CE,EAAAC,EAASH,EAAA,MACrDA,EAAAI,SAAAF,EAAAG,QAAAL,EAAAI,SAGAE,EADUL,EAAQ,GAAgEM,SAClF,WAAAP,GAAA,4BCRAE,EAAAG,QAA2BJ,EAAQ,EAARA,EAA0D,IAKrFO,KAAA,CAAcN,EAAAC,EAAS,4iBAA4iB,0DC8CpjBM,EA/CO,CACpBC,WAAY,CACVC,qBAEFC,KAJoB,WAKlB,MAAO,CACLC,KAAM,CACJC,SAAU,IAEZC,KAAM,KAGVC,SAAU,CACRC,KADQ,WAEN,OAAOC,KAAKC,OAAOC,MAAMC,SAASP,UAAY,KAGlDQ,QAAS,CACPC,MADO,WAELL,KAAKL,KAAO,CACVC,SAAU,KAGdU,KANO,SAMDC,EAASC,GAAM,IAAAC,EAAAT,KACbU,EAAQV,KAAKC,OAEnBU,MAAMJ,GACHK,KAAK,SAACC,GACLA,EAAIC,OAAOF,KAAK,SAACE,GACf,IAAIC,EAAO,IAAIC,KAAK,CAACF,GAAON,EAAM,CAAES,SAAU,cAC1CC,EAAW,IAAIC,SACnBD,EAASE,OAAO,OAAQL,GACxBM,IAAoBC,YAAY,CAAEZ,QAAOQ,aACtCN,KAAK,SAACW,GACLd,EAAKe,MAAM,WAAYD,GACvBd,EAAKJ,SACJ,SAACoB,GACFC,QAAQC,KAAK,wBACbD,QAAQC,KAAKF,GACbhB,EAAKe,MAAM,gBAAiB,2BCnC5C,IAEAI,EAVA,SAAAC,GACE9C,EAAQ,MAeV+C,EAAgBC,OAAAC,EAAA,EAAAD,CACdE,ECjBF,WAA0B,IAAAC,EAAAlC,KAAamC,EAAAD,EAAAE,eAA0BC,EAAAH,EAAAI,MAAAD,IAAAF,EAAwB,OAAAE,EAAA,OAAiBE,YAAA,kBAA6B,CAAAF,EAAA,gBAAqBE,YAAA,eAAAC,MAAA,CAAkCC,uBAAA,EAAAC,kBAAA,KAAiDR,EAAAS,GAAAT,EAAA,cAAAU,GAAyC,OAAAP,EAAA,OAAiBQ,IAAAD,EAAA/C,KAAA0C,YAAA,yBAAAC,MAAA,CAAiEM,gBAAAF,EAAAjD,KAAAoD,MAAAC,MAAAJ,EAAA/C,KAAA+C,EAAAjD,KAAAsD,UAA4Ff,EAAAS,GAAAC,EAAAjD,KAAA,kBAAAY,GAAsD,OAAA8B,EAAA,OAAiBQ,IAAAtC,EAAAgC,YAAA,UAAAW,GAAA,CAAsCC,MAAA,SAAAC,GAAyBA,EAAAC,kBAAyBD,EAAAE,iBAAwBpB,EAAA5B,KAAAsC,EAAA/C,KAAAU,EAAAqC,EAAAjD,KAAAoD,UAA+D,CAAAV,EAAA,OAAYG,MAAA,CAAOe,IAAAX,EAAA/C,KAAAU,SAAsC,KAAK,QAC1vB,IDOA,EAaAqB,EATA,KAEA,MAYe4B,EAAA,QAAA1B,EAAiB","file":"static/js/2.93c984e8c993f92c77a1.js","sourcesContent":["// style-loader: Adds some css to the DOM by adding a \n","function injectStyle (context) {\n require(\"!!vue-style-loader!css-loader?minimize!../../../node_modules/vue-loader/lib/style-compiler/index?{\\\"optionsId\\\":\\\"0\\\",\\\"vue\\\":true,\\\"scoped\\\":false,\\\"sourceMap\\\":false}!sass-loader!../../../node_modules/vue-loader/lib/selector?type=styles&index=0!./checkbox.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!../../../node_modules/vue-loader/lib/selector?type=script&index=0!./checkbox.vue\"\nimport __vue_script__ from \"!!babel-loader!../../../node_modules/vue-loader/lib/selector?type=script&index=0!./checkbox.vue\"\n/* template */\nimport {render as __vue_render__, staticRenderFns as __vue_static_render_fns__} from \"!!../../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-01a5cae8\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./checkbox.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nimport normalizeComponent from \"!../../../node_modules/vue-loader/lib/runtime/component-normalizer\"\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_render__,\n __vue_static_render_fns__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('label',{staticClass:\"checkbox\",class:{ disabled: _vm.disabled, indeterminate: _vm.indeterminate }},[_c('input',{attrs:{\"type\":\"checkbox\",\"disabled\":_vm.disabled},domProps:{\"checked\":_vm.checked,\"indeterminate\":_vm.indeterminate},on:{\"change\":function($event){_vm.$emit('change', $event.target.checked)}}}),_vm._v(\" \"),_c('i',{staticClass:\"checkbox-indicator\"}),_vm._v(\" \"),(!!_vm.$slots.default)?_c('span',{staticClass:\"label\"},[_vm._t(\"default\")],2):_vm._e()])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","import { filter, sortBy, includes } from 'lodash'\n\nexport const notificationsFromStore = store => store.state.statuses.notifications.data\n\nexport const visibleTypes = store => ([\n store.state.config.notificationVisibility.likes && 'like',\n store.state.config.notificationVisibility.mentions && 'mention',\n store.state.config.notificationVisibility.repeats && 'repeat',\n store.state.config.notificationVisibility.follows && 'follow',\n store.state.config.notificationVisibility.followRequest && 'follow_request',\n store.state.config.notificationVisibility.moves && 'move',\n store.state.config.notificationVisibility.emojiReactions && 'pleroma:emoji_reaction'\n].filter(_ => _))\n\nconst statusNotifications = ['like', 'mention', 'repeat', 'pleroma:emoji_reaction']\n\nexport const isStatusNotification = (type) => includes(statusNotifications, type)\n\nconst sortById = (a, b) => {\n const seqA = Number(a.id)\n const seqB = Number(b.id)\n const isSeqA = !Number.isNaN(seqA)\n const isSeqB = !Number.isNaN(seqB)\n if (isSeqA && isSeqB) {\n return seqA > seqB ? -1 : 1\n } else if (isSeqA && !isSeqB) {\n return 1\n } else if (!isSeqA && isSeqB) {\n return -1\n } else {\n return a.id > b.id ? -1 : 1\n }\n}\n\nexport const filteredNotificationsFromStore = (store, types) => {\n // map is just to clone the array since sort mutates it and it causes some issues\n let sortedNotifications = notificationsFromStore(store).map(_ => _).sort(sortById)\n sortedNotifications = sortBy(sortedNotifications, 'seen')\n return sortedNotifications.filter(\n (notification) => (types || visibleTypes(store)).includes(notification.type)\n )\n}\n\nexport const unseenNotificationsFromStore = store =>\n filter(filteredNotificationsFromStore(store), ({ seen }) => !seen)\n","// TODO this func might as well take the entire file and use its mimetype\n// or the entire service could be just mimetype service that only operates\n// on mimetypes and not files. Currently the naming is confusing.\nconst fileType = mimetype => {\n if (mimetype.match(/text\\/html/)) {\n return 'html'\n }\n\n if (mimetype.match(/image/)) {\n return 'image'\n }\n\n if (mimetype.match(/video/)) {\n return 'video'\n }\n\n if (mimetype.match(/audio/)) {\n return 'audio'\n }\n\n return 'unknown'\n}\n\nconst fileMatchesSomeType = (types, file) =>\n types.some(type => fileType(file.mimetype) === type)\n\nconst fileTypeService = {\n fileType,\n fileMatchesSomeType\n}\n\nexport default fileTypeService\n","import { includes } from 'lodash'\n\nconst generateProfileLink = (id, screenName, restrictedNicknames) => {\n const complicated = !screenName || (isExternal(screenName) || includes(restrictedNicknames, screenName))\n return {\n name: (complicated ? 'external-user-profile' : 'user-profile'),\n params: (complicated ? { id } : { name: screenName })\n }\n}\n\nconst isExternal = screenName => screenName && screenName.includes('@')\n\nexport default generateProfileLink\n","const DialogModal = {\n props: {\n darkOverlay: {\n default: true,\n type: Boolean\n },\n onCancel: {\n default: () => {},\n type: Function\n }\n }\n}\n\nexport default DialogModal\n","function injectStyle (context) {\n require(\"!!vue-style-loader!css-loader?minimize!../../../node_modules/vue-loader/lib/style-compiler/index?{\\\"optionsId\\\":\\\"0\\\",\\\"vue\\\":true,\\\"scoped\\\":false,\\\"sourceMap\\\":false}!sass-loader!../../../node_modules/vue-loader/lib/selector?type=styles&index=0!./dialog_modal.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./dialog_modal.js\"\nimport __vue_script__ from \"!!babel-loader!./dialog_modal.js\"/* template */\nimport {render as __vue_render__, staticRenderFns as __vue_static_render_fns__} from \"!!../../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-70b9d662\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./dialog_modal.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nimport normalizeComponent from \"!../../../node_modules/vue-loader/lib/runtime/component-normalizer\"\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_render__,\n __vue_static_render_fns__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('span',{class:{ 'dark-overlay': _vm.darkOverlay },on:{\"click\":function($event){if($event.target !== $event.currentTarget){ return null; }$event.stopPropagation();_vm.onCancel()}}},[_c('div',{staticClass:\"dialog-modal panel panel-default\",on:{\"click\":function($event){$event.stopPropagation();}}},[_c('div',{staticClass:\"panel-heading dialog-modal-heading\"},[_c('div',{staticClass:\"title\"},[_vm._t(\"header\")],2)]),_vm._v(\" \"),_c('div',{staticClass:\"dialog-modal-content\"},[_vm._t(\"default\")],2),_vm._v(\" \"),_c('div',{staticClass:\"dialog-modal-footer user-interactions panel-footer\"},[_vm._t(\"footer\")],2)])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","import DialogModal from '../dialog_modal/dialog_modal.vue'\nimport Popover from '../popover/popover.vue'\n\nconst FORCE_NSFW = 'mrf_tag:media-force-nsfw'\nconst STRIP_MEDIA = 'mrf_tag:media-strip'\nconst FORCE_UNLISTED = 'mrf_tag:force-unlisted'\nconst DISABLE_REMOTE_SUBSCRIPTION = 'mrf_tag:disable-remote-subscription'\nconst DISABLE_ANY_SUBSCRIPTION = 'mrf_tag:disable-any-subscription'\nconst SANDBOX = 'mrf_tag:sandbox'\nconst QUARANTINE = 'mrf_tag:quarantine'\n\nconst ModerationTools = {\n props: [\n 'user'\n ],\n data () {\n return {\n tags: {\n FORCE_NSFW,\n STRIP_MEDIA,\n FORCE_UNLISTED,\n DISABLE_REMOTE_SUBSCRIPTION,\n DISABLE_ANY_SUBSCRIPTION,\n SANDBOX,\n QUARANTINE\n },\n showDeleteUserDialog: false,\n toggled: false\n }\n },\n components: {\n DialogModal,\n Popover\n },\n computed: {\n tagsSet () {\n return new Set(this.user.tags)\n },\n hasTagPolicy () {\n return this.$store.state.instance.tagPolicyAvailable\n }\n },\n methods: {\n hasTag (tagName) {\n return this.tagsSet.has(tagName)\n },\n toggleTag (tag) {\n const store = this.$store\n if (this.tagsSet.has(tag)) {\n store.state.api.backendInteractor.untagUser({ user: this.user, tag }).then(response => {\n if (!response.ok) { return }\n store.commit('untagUser', { user: this.user, tag })\n })\n } else {\n store.state.api.backendInteractor.tagUser({ user: this.user, tag }).then(response => {\n if (!response.ok) { return }\n store.commit('tagUser', { user: this.user, tag })\n })\n }\n },\n toggleRight (right) {\n const store = this.$store\n if (this.user.rights[right]) {\n store.state.api.backendInteractor.deleteRight({ user: this.user, right }).then(response => {\n if (!response.ok) { return }\n store.commit('updateRight', { user: this.user, right, value: false })\n })\n } else {\n store.state.api.backendInteractor.addRight({ user: this.user, right }).then(response => {\n if (!response.ok) { return }\n store.commit('updateRight', { user: this.user, right, value: true })\n })\n }\n },\n toggleActivationStatus () {\n this.$store.dispatch('toggleActivationStatus', { user: this.user })\n },\n deleteUserDialog (show) {\n this.showDeleteUserDialog = show\n },\n deleteUser () {\n const store = this.$store\n const user = this.user\n const { id, name } = user\n store.state.api.backendInteractor.deleteUser({ user })\n .then(e => {\n this.$store.dispatch('markStatusesAsDeleted', status => user.id === status.user.id)\n const isProfile = this.$route.name === 'external-user-profile' || this.$route.name === 'user-profile'\n const isTargetUser = this.$route.params.name === name || this.$route.params.id === id\n if (isProfile && isTargetUser) {\n window.history.back()\n }\n })\n },\n setToggled (value) {\n this.toggled = value\n }\n }\n}\n\nexport default ModerationTools\n","function injectStyle (context) {\n require(\"!!vue-style-loader!css-loader?minimize!../../../node_modules/vue-loader/lib/style-compiler/index?{\\\"optionsId\\\":\\\"0\\\",\\\"vue\\\":true,\\\"scoped\\\":false,\\\"sourceMap\\\":false}!sass-loader!../../../node_modules/vue-loader/lib/selector?type=styles&index=0!./moderation_tools.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./moderation_tools.js\"\nimport __vue_script__ from \"!!babel-loader!./moderation_tools.js\"/* template */\nimport {render as __vue_render__, staticRenderFns as __vue_static_render_fns__} from \"!!../../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-168f1ca6\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./moderation_tools.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nimport normalizeComponent from \"!../../../node_modules/vue-loader/lib/runtime/component-normalizer\"\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_render__,\n __vue_static_render_fns__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('Popover',{staticClass:\"moderation-tools-popover\",attrs:{\"trigger\":\"click\",\"placement\":\"bottom\",\"offset\":{ y: 5 }},on:{\"show\":function($event){_vm.setToggled(true)},\"close\":function($event){_vm.setToggled(false)}}},[_c('div',{attrs:{\"slot\":\"content\"},slot:\"content\"},[_c('div',{staticClass:\"dropdown-menu\"},[(_vm.user.is_local)?_c('span',[_c('button',{staticClass:\"dropdown-item\",on:{\"click\":function($event){_vm.toggleRight(\"admin\")}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(!!_vm.user.rights.admin ? 'user_card.admin_menu.revoke_admin' : 'user_card.admin_menu.grant_admin'))+\"\\n \")]),_vm._v(\" \"),_c('button',{staticClass:\"dropdown-item\",on:{\"click\":function($event){_vm.toggleRight(\"moderator\")}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(!!_vm.user.rights.moderator ? 'user_card.admin_menu.revoke_moderator' : 'user_card.admin_menu.grant_moderator'))+\"\\n \")]),_vm._v(\" \"),_c('div',{staticClass:\"dropdown-divider\",attrs:{\"role\":\"separator\"}})]):_vm._e(),_vm._v(\" \"),_c('button',{staticClass:\"dropdown-item\",on:{\"click\":function($event){_vm.toggleActivationStatus()}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(!!_vm.user.deactivated ? 'user_card.admin_menu.activate_account' : 'user_card.admin_menu.deactivate_account'))+\"\\n \")]),_vm._v(\" \"),_c('button',{staticClass:\"dropdown-item\",on:{\"click\":function($event){_vm.deleteUserDialog(true)}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.admin_menu.delete_account'))+\"\\n \")]),_vm._v(\" \"),(_vm.hasTagPolicy)?_c('div',{staticClass:\"dropdown-divider\",attrs:{\"role\":\"separator\"}}):_vm._e(),_vm._v(\" \"),(_vm.hasTagPolicy)?_c('span',[_c('button',{staticClass:\"dropdown-item\",on:{\"click\":function($event){_vm.toggleTag(_vm.tags.FORCE_NSFW)}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.admin_menu.force_nsfw'))+\"\\n \"),_c('span',{staticClass:\"menu-checkbox\",class:{ 'menu-checkbox-checked': _vm.hasTag(_vm.tags.FORCE_NSFW) }})]),_vm._v(\" \"),_c('button',{staticClass:\"dropdown-item\",on:{\"click\":function($event){_vm.toggleTag(_vm.tags.STRIP_MEDIA)}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.admin_menu.strip_media'))+\"\\n \"),_c('span',{staticClass:\"menu-checkbox\",class:{ 'menu-checkbox-checked': _vm.hasTag(_vm.tags.STRIP_MEDIA) }})]),_vm._v(\" \"),_c('button',{staticClass:\"dropdown-item\",on:{\"click\":function($event){_vm.toggleTag(_vm.tags.FORCE_UNLISTED)}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.admin_menu.force_unlisted'))+\"\\n \"),_c('span',{staticClass:\"menu-checkbox\",class:{ 'menu-checkbox-checked': _vm.hasTag(_vm.tags.FORCE_UNLISTED) }})]),_vm._v(\" \"),_c('button',{staticClass:\"dropdown-item\",on:{\"click\":function($event){_vm.toggleTag(_vm.tags.SANDBOX)}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.admin_menu.sandbox'))+\"\\n \"),_c('span',{staticClass:\"menu-checkbox\",class:{ 'menu-checkbox-checked': _vm.hasTag(_vm.tags.SANDBOX) }})]),_vm._v(\" \"),(_vm.user.is_local)?_c('button',{staticClass:\"dropdown-item\",on:{\"click\":function($event){_vm.toggleTag(_vm.tags.DISABLE_REMOTE_SUBSCRIPTION)}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.admin_menu.disable_remote_subscription'))+\"\\n \"),_c('span',{staticClass:\"menu-checkbox\",class:{ 'menu-checkbox-checked': _vm.hasTag(_vm.tags.DISABLE_REMOTE_SUBSCRIPTION) }})]):_vm._e(),_vm._v(\" \"),(_vm.user.is_local)?_c('button',{staticClass:\"dropdown-item\",on:{\"click\":function($event){_vm.toggleTag(_vm.tags.DISABLE_ANY_SUBSCRIPTION)}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.admin_menu.disable_any_subscription'))+\"\\n \"),_c('span',{staticClass:\"menu-checkbox\",class:{ 'menu-checkbox-checked': _vm.hasTag(_vm.tags.DISABLE_ANY_SUBSCRIPTION) }})]):_vm._e(),_vm._v(\" \"),(_vm.user.is_local)?_c('button',{staticClass:\"dropdown-item\",on:{\"click\":function($event){_vm.toggleTag(_vm.tags.QUARANTINE)}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.admin_menu.quarantine'))+\"\\n \"),_c('span',{staticClass:\"menu-checkbox\",class:{ 'menu-checkbox-checked': _vm.hasTag(_vm.tags.QUARANTINE) }})]):_vm._e()]):_vm._e()])]),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-default btn-block\",class:{ toggled: _vm.toggled },attrs:{\"slot\":\"trigger\"},slot:\"trigger\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.admin_menu.moderation'))+\"\\n \")])]),_vm._v(\" \"),_c('portal',{attrs:{\"to\":\"modal\"}},[(_vm.showDeleteUserDialog)?_c('DialogModal',{attrs:{\"on-cancel\":_vm.deleteUserDialog.bind(this, false)}},[_c('template',{slot:\"header\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.admin_menu.delete_user'))+\"\\n \")]),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('user_card.admin_menu.delete_user_confirmation')))]),_vm._v(\" \"),_c('template',{slot:\"footer\"},[_c('button',{staticClass:\"btn btn-default\",on:{\"click\":function($event){_vm.deleteUserDialog(false)}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.cancel'))+\"\\n \")]),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-default danger\",on:{\"click\":function($event){_vm.deleteUser()}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.admin_menu.delete_user'))+\"\\n \")])])],2):_vm._e()],1)],1)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","import ProgressButton from '../progress_button/progress_button.vue'\nimport Popover from '../popover/popover.vue'\n\nconst AccountActions = {\n props: [\n 'user'\n ],\n data () {\n return { }\n },\n components: {\n ProgressButton,\n Popover\n },\n methods: {\n showRepeats () {\n this.$store.dispatch('showReblogs', this.user.id)\n },\n hideRepeats () {\n this.$store.dispatch('hideReblogs', this.user.id)\n },\n blockUser () {\n this.$store.dispatch('blockUser', this.user.id)\n },\n unblockUser () {\n this.$store.dispatch('unblockUser', this.user.id)\n },\n reportUser () {\n this.$store.dispatch('openUserReportingModal', this.user.id)\n }\n }\n}\n\nexport default AccountActions\n","function injectStyle (context) {\n require(\"!!vue-style-loader!css-loader?minimize!../../../node_modules/vue-loader/lib/style-compiler/index?{\\\"optionsId\\\":\\\"0\\\",\\\"vue\\\":true,\\\"scoped\\\":false,\\\"sourceMap\\\":false}!sass-loader!../../../node_modules/vue-loader/lib/selector?type=styles&index=0!./account_actions.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./account_actions.js\"\nimport __vue_script__ from \"!!babel-loader!./account_actions.js\"/* template */\nimport {render as __vue_render__, staticRenderFns as __vue_static_render_fns__} from \"!!../../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-875a9014\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./account_actions.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nimport normalizeComponent from \"!../../../node_modules/vue-loader/lib/runtime/component-normalizer\"\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_render__,\n __vue_static_render_fns__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"account-actions\"},[_c('Popover',{attrs:{\"trigger\":\"click\",\"placement\":\"bottom\"}},[_c('div',{staticClass:\"account-tools-popover\",attrs:{\"slot\":\"content\"},slot:\"content\"},[_c('div',{staticClass:\"dropdown-menu\"},[(_vm.user.following)?[(_vm.user.showing_reblogs)?_c('button',{staticClass:\"btn btn-default dropdown-item\",on:{\"click\":_vm.hideRepeats}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.hide_repeats'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(!_vm.user.showing_reblogs)?_c('button',{staticClass:\"btn btn-default dropdown-item\",on:{\"click\":_vm.showRepeats}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.show_repeats'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),_c('div',{staticClass:\"dropdown-divider\",attrs:{\"role\":\"separator\"}})]:_vm._e(),_vm._v(\" \"),(_vm.user.statusnet_blocking)?_c('button',{staticClass:\"btn btn-default btn-block dropdown-item\",on:{\"click\":_vm.unblockUser}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.unblock'))+\"\\n \")]):_c('button',{staticClass:\"btn btn-default btn-block dropdown-item\",on:{\"click\":_vm.blockUser}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.block'))+\"\\n \")]),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-default btn-block dropdown-item\",on:{\"click\":_vm.reportUser}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.report'))+\"\\n \")])],2)]),_vm._v(\" \"),_c('div',{staticClass:\"btn btn-default ellipsis-button\",attrs:{\"slot\":\"trigger\"},slot:\"trigger\"},[_c('i',{staticClass:\"icon-ellipsis trigger-button\"})])])],1)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","import UserAvatar from '../user_avatar/user_avatar.vue'\nimport RemoteFollow from '../remote_follow/remote_follow.vue'\nimport ProgressButton from '../progress_button/progress_button.vue'\nimport FollowButton from '../follow_button/follow_button.vue'\nimport ModerationTools from '../moderation_tools/moderation_tools.vue'\nimport AccountActions from '../account_actions/account_actions.vue'\nimport generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'\nimport { mapGetters } from 'vuex'\n\nexport default {\n props: [\n 'user', 'switcher', 'selected', 'hideBio', 'rounded', 'bordered', 'allowZoomingAvatar'\n ],\n data () {\n return {\n followRequestInProgress: false,\n betterShadow: this.$store.state.interface.browserSupport.cssFilter\n }\n },\n created () {\n this.$store.dispatch('fetchUserRelationship', this.user.id)\n },\n computed: {\n classes () {\n return [{\n 'user-card-rounded-t': this.rounded === 'top', // set border-top-left-radius and border-top-right-radius\n 'user-card-rounded': this.rounded === true, // set border-radius for all sides\n 'user-card-bordered': this.bordered === true // set border for all sides\n }]\n },\n style () {\n return {\n backgroundImage: [\n `linear-gradient(to bottom, var(--profileTint), var(--profileTint))`,\n `url(${this.user.cover_photo})`\n ].join(', ')\n }\n },\n isOtherUser () {\n return this.user.id !== this.$store.state.users.currentUser.id\n },\n subscribeUrl () {\n // eslint-disable-next-line no-undef\n const serverUrl = new URL(this.user.statusnet_profile_url)\n return `${serverUrl.protocol}//${serverUrl.host}/main/ostatus`\n },\n loggedIn () {\n return this.$store.state.users.currentUser\n },\n dailyAvg () {\n const days = Math.ceil((new Date() - new Date(this.user.created_at)) / (60 * 60 * 24 * 1000))\n return Math.round(this.user.statuses_count / days)\n },\n userHighlightType: {\n get () {\n const data = this.$store.getters.mergedConfig.highlight[this.user.screen_name]\n return (data && data.type) || 'disabled'\n },\n set (type) {\n const data = this.$store.getters.mergedConfig.highlight[this.user.screen_name]\n if (type !== 'disabled') {\n this.$store.dispatch('setHighlight', { user: this.user.screen_name, color: (data && data.color) || '#FFFFFF', type })\n } else {\n this.$store.dispatch('setHighlight', { user: this.user.screen_name, color: undefined })\n }\n },\n ...mapGetters(['mergedConfig'])\n },\n userHighlightColor: {\n get () {\n const data = this.$store.getters.mergedConfig.highlight[this.user.screen_name]\n return data && data.color\n },\n set (color) {\n this.$store.dispatch('setHighlight', { user: this.user.screen_name, color })\n }\n },\n visibleRole () {\n const rights = this.user.rights\n if (!rights) { return }\n const validRole = rights.admin || rights.moderator\n const roleTitle = rights.admin ? 'admin' : 'moderator'\n return validRole && roleTitle\n },\n hideFollowsCount () {\n return this.isOtherUser && this.user.hide_follows_count\n },\n hideFollowersCount () {\n return this.isOtherUser && this.user.hide_followers_count\n },\n ...mapGetters(['mergedConfig'])\n },\n components: {\n UserAvatar,\n RemoteFollow,\n ModerationTools,\n AccountActions,\n ProgressButton,\n FollowButton\n },\n methods: {\n muteUser () {\n this.$store.dispatch('muteUser', this.user.id)\n },\n unmuteUser () {\n this.$store.dispatch('unmuteUser', this.user.id)\n },\n subscribeUser () {\n return this.$store.dispatch('subscribeUser', this.user.id)\n },\n unsubscribeUser () {\n return this.$store.dispatch('unsubscribeUser', this.user.id)\n },\n setProfileView (v) {\n if (this.switcher) {\n const store = this.$store\n store.commit('setProfileView', { v })\n }\n },\n linkClicked ({ target }) {\n if (target.tagName === 'SPAN') {\n target = target.parentNode\n }\n if (target.tagName === 'A') {\n window.open(target.href, '_blank')\n }\n },\n userProfileLink (user) {\n return generateProfileLink(\n user.id, user.screen_name,\n this.$store.state.instance.restrictedNicknames\n )\n },\n zoomAvatar () {\n const attachment = {\n url: this.user.profile_image_url_original,\n mimetype: 'image'\n }\n this.$store.dispatch('setMedia', [attachment])\n this.$store.dispatch('setCurrent', attachment)\n },\n mentionUser () {\n this.$store.dispatch('openPostStatusModal', { replyTo: true, repliedUser: this.user })\n }\n }\n}\n","function injectStyle (context) {\n require(\"!!vue-style-loader!css-loader?minimize!../../../node_modules/vue-loader/lib/style-compiler/index?{\\\"optionsId\\\":\\\"0\\\",\\\"vue\\\":true,\\\"scoped\\\":false,\\\"sourceMap\\\":false}!sass-loader!../../../node_modules/vue-loader/lib/selector?type=styles&index=0!./user_card.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./user_card.js\"\nimport __vue_script__ from \"!!babel-loader!./user_card.js\"/* template */\nimport {render as __vue_render__, staticRenderFns as __vue_static_render_fns__} from \"!!../../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-e977a532\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./user_card.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nimport normalizeComponent from \"!../../../node_modules/vue-loader/lib/runtime/component-normalizer\"\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_render__,\n __vue_static_render_fns__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"user-card\",class:_vm.classes},[_c('div',{staticClass:\"background-image\",class:{ 'hide-bio': _vm.hideBio },style:(_vm.style)}),_vm._v(\" \"),_c('div',{staticClass:\"panel-heading\"},[_c('div',{staticClass:\"user-info\"},[_c('div',{staticClass:\"container\"},[(_vm.allowZoomingAvatar)?_c('a',{staticClass:\"user-info-avatar-link\",on:{\"click\":_vm.zoomAvatar}},[_c('UserAvatar',{attrs:{\"better-shadow\":_vm.betterShadow,\"user\":_vm.user}}),_vm._v(\" \"),_vm._m(0)],1):_c('router-link',{attrs:{\"to\":_vm.userProfileLink(_vm.user)}},[_c('UserAvatar',{attrs:{\"better-shadow\":_vm.betterShadow,\"user\":_vm.user}})],1),_vm._v(\" \"),_c('div',{staticClass:\"user-summary\"},[_c('div',{staticClass:\"top-line\"},[(_vm.user.name_html)?_c('div',{staticClass:\"user-name\",attrs:{\"title\":_vm.user.name},domProps:{\"innerHTML\":_vm._s(_vm.user.name_html)}}):_c('div',{staticClass:\"user-name\",attrs:{\"title\":_vm.user.name}},[_vm._v(\"\\n \"+_vm._s(_vm.user.name)+\"\\n \")]),_vm._v(\" \"),(!_vm.isOtherUser)?_c('router-link',{attrs:{\"to\":{ name: 'user-settings' }}},[_c('i',{staticClass:\"button-icon icon-wrench usersettings\",attrs:{\"title\":_vm.$t('tool_tip.user_settings')}})]):_vm._e(),_vm._v(\" \"),(_vm.isOtherUser && !_vm.user.is_local)?_c('a',{attrs:{\"href\":_vm.user.statusnet_profile_url,\"target\":\"_blank\"}},[_c('i',{staticClass:\"icon-link-ext usersettings\"})]):_vm._e(),_vm._v(\" \"),(_vm.isOtherUser && _vm.loggedIn)?_c('AccountActions',{attrs:{\"user\":_vm.user}}):_vm._e()],1),_vm._v(\" \"),_c('div',{staticClass:\"bottom-line\"},[_c('router-link',{staticClass:\"user-screen-name\",attrs:{\"to\":_vm.userProfileLink(_vm.user)}},[_vm._v(\"\\n @\"+_vm._s(_vm.user.screen_name)+\"\\n \")]),_vm._v(\" \"),(!_vm.hideBio && !!_vm.visibleRole)?_c('span',{staticClass:\"alert staff\"},[_vm._v(_vm._s(_vm.visibleRole))]):_vm._e(),_vm._v(\" \"),(_vm.user.locked)?_c('span',[_c('i',{staticClass:\"icon icon-lock\"})]):_vm._e(),_vm._v(\" \"),(!_vm.mergedConfig.hideUserStats && !_vm.hideBio)?_c('span',{staticClass:\"dailyAvg\"},[_vm._v(_vm._s(_vm.dailyAvg)+\" \"+_vm._s(_vm.$t('user_card.per_day')))]):_vm._e()],1)])],1),_vm._v(\" \"),_c('div',{staticClass:\"user-meta\"},[(_vm.user.follows_you && _vm.loggedIn && _vm.isOtherUser)?_c('div',{staticClass:\"following\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.follows_you'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(_vm.isOtherUser && (_vm.loggedIn || !_vm.switcher))?_c('div',{staticClass:\"highlighter\"},[(_vm.userHighlightType !== 'disabled')?_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.userHighlightColor),expression:\"userHighlightColor\"}],staticClass:\"userHighlightText\",attrs:{\"id\":'userHighlightColorTx'+_vm.user.id,\"type\":\"text\"},domProps:{\"value\":(_vm.userHighlightColor)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.userHighlightColor=$event.target.value}}}):_vm._e(),_vm._v(\" \"),(_vm.userHighlightType !== 'disabled')?_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.userHighlightColor),expression:\"userHighlightColor\"}],staticClass:\"userHighlightCl\",attrs:{\"id\":'userHighlightColor'+_vm.user.id,\"type\":\"color\"},domProps:{\"value\":(_vm.userHighlightColor)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.userHighlightColor=$event.target.value}}}):_vm._e(),_vm._v(\" \"),_c('label',{staticClass:\"userHighlightSel select\",attrs:{\"for\":\"style-switcher\"}},[_c('select',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.userHighlightType),expression:\"userHighlightType\"}],staticClass:\"userHighlightSel\",attrs:{\"id\":'userHighlightSel'+_vm.user.id},on:{\"change\":function($event){var $$selectedVal = Array.prototype.filter.call($event.target.options,function(o){return o.selected}).map(function(o){var val = \"_value\" in o ? o._value : o.value;return val}); _vm.userHighlightType=$event.target.multiple ? $$selectedVal : $$selectedVal[0]}}},[_c('option',{attrs:{\"value\":\"disabled\"}},[_vm._v(\"No highlight\")]),_vm._v(\" \"),_c('option',{attrs:{\"value\":\"solid\"}},[_vm._v(\"Solid bg\")]),_vm._v(\" \"),_c('option',{attrs:{\"value\":\"striped\"}},[_vm._v(\"Striped bg\")]),_vm._v(\" \"),_c('option',{attrs:{\"value\":\"side\"}},[_vm._v(\"Side stripe\")])]),_vm._v(\" \"),_c('i',{staticClass:\"icon-down-open\"})])]):_vm._e()]),_vm._v(\" \"),(_vm.loggedIn && _vm.isOtherUser)?_c('div',{staticClass:\"user-interactions\"},[_c('div',{staticClass:\"btn-group\"},[_c('FollowButton',{attrs:{\"user\":_vm.user}}),_vm._v(\" \"),(_vm.user.following)?[(!_vm.user.subscribed)?_c('ProgressButton',{staticClass:\"btn btn-default\",attrs:{\"click\":_vm.subscribeUser,\"title\":_vm.$t('user_card.subscribe')}},[_c('i',{staticClass:\"icon-bell-alt\"})]):_c('ProgressButton',{staticClass:\"btn btn-default toggled\",attrs:{\"click\":_vm.unsubscribeUser,\"title\":_vm.$t('user_card.unsubscribe')}},[_c('i',{staticClass:\"icon-bell-ringing-o\"})])]:_vm._e()],2),_vm._v(\" \"),_c('div',[(_vm.user.muted)?_c('button',{staticClass:\"btn btn-default btn-block toggled\",on:{\"click\":_vm.unmuteUser}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.muted'))+\"\\n \")]):_c('button',{staticClass:\"btn btn-default btn-block\",on:{\"click\":_vm.muteUser}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.mute'))+\"\\n \")])]),_vm._v(\" \"),_c('div',[_c('button',{staticClass:\"btn btn-default btn-block\",on:{\"click\":_vm.mentionUser}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.mention'))+\"\\n \")])]),_vm._v(\" \"),(_vm.loggedIn.role === \"admin\")?_c('ModerationTools',{attrs:{\"user\":_vm.user}}):_vm._e()],1):_vm._e(),_vm._v(\" \"),(!_vm.loggedIn && _vm.user.is_local)?_c('div',{staticClass:\"user-interactions\"},[_c('RemoteFollow',{attrs:{\"user\":_vm.user}})],1):_vm._e()])]),_vm._v(\" \"),(!_vm.hideBio)?_c('div',{staticClass:\"panel-body\"},[(!_vm.mergedConfig.hideUserStats && _vm.switcher)?_c('div',{staticClass:\"user-counts\"},[_c('div',{staticClass:\"user-count\",on:{\"click\":function($event){$event.preventDefault();_vm.setProfileView('statuses')}}},[_c('h5',[_vm._v(_vm._s(_vm.$t('user_card.statuses')))]),_vm._v(\" \"),_c('span',[_vm._v(_vm._s(_vm.user.statuses_count)+\" \"),_c('br')])]),_vm._v(\" \"),_c('div',{staticClass:\"user-count\",on:{\"click\":function($event){$event.preventDefault();_vm.setProfileView('friends')}}},[_c('h5',[_vm._v(_vm._s(_vm.$t('user_card.followees')))]),_vm._v(\" \"),_c('span',[_vm._v(_vm._s(_vm.hideFollowsCount ? _vm.$t('user_card.hidden') : _vm.user.friends_count))])]),_vm._v(\" \"),_c('div',{staticClass:\"user-count\",on:{\"click\":function($event){$event.preventDefault();_vm.setProfileView('followers')}}},[_c('h5',[_vm._v(_vm._s(_vm.$t('user_card.followers')))]),_vm._v(\" \"),_c('span',[_vm._v(_vm._s(_vm.hideFollowersCount ? _vm.$t('user_card.hidden') : _vm.user.followers_count))])])]):_vm._e(),_vm._v(\" \"),(!_vm.hideBio && _vm.user.description_html)?_c('p',{staticClass:\"user-card-bio\",domProps:{\"innerHTML\":_vm._s(_vm.user.description_html)},on:{\"click\":function($event){$event.preventDefault();return _vm.linkClicked($event)}}}):(!_vm.hideBio)?_c('p',{staticClass:\"user-card-bio\"},[_vm._v(\"\\n \"+_vm._s(_vm.user.description)+\"\\n \")]):_vm._e()]):_vm._e()])}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"user-info-avatar-link-overlay\"},[_c('i',{staticClass:\"button-icon icon-zoom-in\"})])}]\nexport { render, staticRenderFns }","import StillImage from '../still-image/still-image.vue'\n\nconst UserAvatar = {\n props: [\n 'user',\n 'betterShadow',\n 'compact'\n ],\n data () {\n return {\n showPlaceholder: false\n }\n },\n components: {\n StillImage\n },\n computed: {\n imgSrc () {\n return this.showPlaceholder ? '/images/avi.png' : this.user.profile_image_url_original\n }\n },\n methods: {\n imageLoadError () {\n this.showPlaceholder = true\n }\n },\n watch: {\n src () {\n this.showPlaceholder = false\n }\n }\n}\n\nexport default UserAvatar\n","function injectStyle (context) {\n require(\"!!vue-style-loader!css-loader?minimize!../../../node_modules/vue-loader/lib/style-compiler/index?{\\\"optionsId\\\":\\\"0\\\",\\\"vue\\\":true,\\\"scoped\\\":false,\\\"sourceMap\\\":false}!sass-loader!../../../node_modules/vue-loader/lib/selector?type=styles&index=0!./user_avatar.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./user_avatar.js\"\nimport __vue_script__ from \"!!babel-loader!./user_avatar.js\"/* template */\nimport {render as __vue_render__, staticRenderFns as __vue_static_render_fns__} from \"!!../../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-056a5e34\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./user_avatar.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nimport normalizeComponent from \"!../../../node_modules/vue-loader/lib/runtime/component-normalizer\"\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_render__,\n __vue_static_render_fns__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('StillImage',{staticClass:\"avatar\",class:{ 'avatar-compact': _vm.compact, 'better-shadow': _vm.betterShadow },attrs:{\"alt\":_vm.user.screen_name,\"title\":_vm.user.screen_name,\"src\":_vm.imgSrc,\"image-load-error\":_vm.imageLoadError}})}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","import StillImage from '../still-image/still-image.vue'\nimport VideoAttachment from '../video_attachment/video_attachment.vue'\nimport nsfwImage from '../../assets/nsfw.png'\nimport fileTypeService from '../../services/file_type/file_type.service.js'\nimport { mapGetters } from 'vuex'\n\nconst Attachment = {\n props: [\n 'attachment',\n 'nsfw',\n 'statusId',\n 'size',\n 'allowPlay',\n 'setMedia',\n 'naturalSizeLoad'\n ],\n data () {\n return {\n nsfwImage: this.$store.state.instance.nsfwCensorImage || nsfwImage,\n hideNsfwLocal: this.$store.getters.mergedConfig.hideNsfw,\n preloadImage: this.$store.getters.mergedConfig.preloadImage,\n loading: false,\n img: fileTypeService.fileType(this.attachment.mimetype) === 'image' && document.createElement('img'),\n modalOpen: false,\n showHidden: false\n }\n },\n components: {\n StillImage,\n VideoAttachment\n },\n computed: {\n usePlaceHolder () {\n return this.size === 'hide' || this.type === 'unknown'\n },\n referrerpolicy () {\n return this.$store.state.instance.mediaProxyAvailable ? '' : 'no-referrer'\n },\n type () {\n return fileTypeService.fileType(this.attachment.mimetype)\n },\n hidden () {\n return this.nsfw && this.hideNsfwLocal && !this.showHidden\n },\n isEmpty () {\n return (this.type === 'html' && !this.attachment.oembed) || this.type === 'unknown'\n },\n isSmall () {\n return this.size === 'small'\n },\n fullwidth () {\n return this.type === 'html' || this.type === 'audio'\n },\n ...mapGetters(['mergedConfig'])\n },\n methods: {\n linkClicked ({ target }) {\n if (target.tagName === 'A') {\n window.open(target.href, '_blank')\n }\n },\n openModal (event) {\n const modalTypes = this.mergedConfig.playVideosInModal\n ? ['image', 'video']\n : ['image']\n if (fileTypeService.fileMatchesSomeType(modalTypes, this.attachment) ||\n this.usePlaceHolder\n ) {\n event.stopPropagation()\n event.preventDefault()\n this.setMedia()\n this.$store.dispatch('setCurrent', this.attachment)\n }\n },\n toggleHidden (event) {\n if (\n (this.mergedConfig.useOneClickNsfw && !this.showHidden) &&\n (this.type !== 'video' || this.mergedConfig.playVideosInModal)\n ) {\n this.openModal(event)\n return\n }\n if (this.img && !this.preloadImage) {\n if (this.img.onload) {\n this.img.onload()\n } else {\n this.loading = true\n this.img.src = this.attachment.url\n this.img.onload = () => {\n this.loading = false\n this.showHidden = !this.showHidden\n }\n }\n } else {\n this.showHidden = !this.showHidden\n }\n },\n onImageLoad (image) {\n const width = image.naturalWidth\n const height = image.naturalHeight\n this.naturalSizeLoad && this.naturalSizeLoad({ width, height })\n }\n }\n}\n\nexport default Attachment\n","function injectStyle (context) {\n require(\"!!vue-style-loader!css-loader?minimize!../../../node_modules/vue-loader/lib/style-compiler/index?{\\\"optionsId\\\":\\\"0\\\",\\\"vue\\\":true,\\\"scoped\\\":false,\\\"sourceMap\\\":false}!sass-loader!../../../node_modules/vue-loader/lib/selector?type=styles&index=0!./attachment.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./attachment.js\"\nimport __vue_script__ from \"!!babel-loader!./attachment.js\"/* template */\nimport {render as __vue_render__, staticRenderFns as __vue_static_render_fns__} from \"!!../../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-61e0eb0c\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./attachment.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nimport normalizeComponent from \"!../../../node_modules/vue-loader/lib/runtime/component-normalizer\"\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_render__,\n __vue_static_render_fns__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n","var render = function () {\nvar _obj;\nvar _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return (_vm.usePlaceHolder)?_c('div',{on:{\"click\":_vm.openModal}},[(_vm.type !== 'html')?_c('a',{staticClass:\"placeholder\",attrs:{\"target\":\"_blank\",\"href\":_vm.attachment.url}},[_vm._v(\"\\n [\"+_vm._s(_vm.nsfw ? \"NSFW/\" : \"\")+_vm._s(_vm.type.toUpperCase())+\"]\\n \")]):_vm._e()]):_c('div',{directives:[{name:\"show\",rawName:\"v-show\",value:(!_vm.isEmpty),expression:\"!isEmpty\"}],staticClass:\"attachment\",class:( _obj = {}, _obj[_vm.type] = true, _obj.loading = _vm.loading, _obj['fullwidth'] = _vm.fullwidth, _obj['nsfw-placeholder'] = _vm.hidden, _obj )},[(_vm.hidden)?_c('a',{staticClass:\"image-attachment\",attrs:{\"href\":_vm.attachment.url},on:{\"click\":function($event){$event.preventDefault();return _vm.toggleHidden($event)}}},[_c('img',{key:_vm.nsfwImage,staticClass:\"nsfw\",class:{'small': _vm.isSmall},attrs:{\"src\":_vm.nsfwImage}}),_vm._v(\" \"),(_vm.type === 'video')?_c('i',{staticClass:\"play-icon icon-play-circled\"}):_vm._e()]):_vm._e(),_vm._v(\" \"),(_vm.nsfw && _vm.hideNsfwLocal && !_vm.hidden)?_c('div',{staticClass:\"hider\"},[_c('a',{attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();return _vm.toggleHidden($event)}}},[_vm._v(\"Hide\")])]):_vm._e(),_vm._v(\" \"),(_vm.type === 'image' && (!_vm.hidden || _vm.preloadImage))?_c('a',{staticClass:\"image-attachment\",class:{'hidden': _vm.hidden && _vm.preloadImage },attrs:{\"href\":_vm.attachment.url,\"target\":\"_blank\",\"title\":_vm.attachment.description},on:{\"click\":_vm.openModal}},[_c('StillImage',{attrs:{\"referrerpolicy\":_vm.referrerpolicy,\"mimetype\":_vm.attachment.mimetype,\"src\":_vm.attachment.large_thumb_url || _vm.attachment.url,\"image-load-handler\":_vm.onImageLoad}})],1):_vm._e(),_vm._v(\" \"),(_vm.type === 'video' && !_vm.hidden)?_c('a',{staticClass:\"video-container\",class:{'small': _vm.isSmall},attrs:{\"href\":_vm.allowPlay ? undefined : _vm.attachment.url},on:{\"click\":_vm.openModal}},[_c('VideoAttachment',{staticClass:\"video\",attrs:{\"attachment\":_vm.attachment,\"controls\":_vm.allowPlay}}),_vm._v(\" \"),(!_vm.allowPlay)?_c('i',{staticClass:\"play-icon icon-play-circled\"}):_vm._e()],1):_vm._e(),_vm._v(\" \"),(_vm.type === 'audio')?_c('audio',{attrs:{\"src\":_vm.attachment.url,\"controls\":\"\"}}):_vm._e(),_vm._v(\" \"),(_vm.type === 'html' && _vm.attachment.oembed)?_c('div',{staticClass:\"oembed\",on:{\"click\":function($event){$event.preventDefault();return _vm.linkClicked($event)}}},[(_vm.attachment.thumb_url)?_c('div',{staticClass:\"image\"},[_c('img',{attrs:{\"src\":_vm.attachment.thumb_url}})]):_vm._e(),_vm._v(\" \"),_c('div',{staticClass:\"text\"},[_c('h1',[_c('a',{attrs:{\"href\":_vm.attachment.url}},[_vm._v(_vm._s(_vm.attachment.oembed.title))])]),_vm._v(\" \"),_c('div',{domProps:{\"innerHTML\":_vm._s(_vm.attachment.oembed.oembedHTML)}})])]):_vm._e()])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","import { mapGetters } from 'vuex'\n\nconst FavoriteButton = {\n props: ['status', 'loggedIn'],\n data () {\n return {\n animated: false\n }\n },\n methods: {\n favorite () {\n if (!this.status.favorited) {\n this.$store.dispatch('favorite', { id: this.status.id })\n } else {\n this.$store.dispatch('unfavorite', { id: this.status.id })\n }\n this.animated = true\n setTimeout(() => {\n this.animated = false\n }, 500)\n }\n },\n computed: {\n classes () {\n return {\n 'icon-star-empty': !this.status.favorited,\n 'icon-star': this.status.favorited,\n 'animate-spin': this.animated\n }\n },\n ...mapGetters(['mergedConfig'])\n }\n}\n\nexport default FavoriteButton\n","function injectStyle (context) {\n require(\"!!vue-style-loader!css-loader?minimize!../../../node_modules/vue-loader/lib/style-compiler/index?{\\\"optionsId\\\":\\\"0\\\",\\\"vue\\\":true,\\\"scoped\\\":false,\\\"sourceMap\\\":false}!sass-loader!../../../node_modules/vue-loader/lib/selector?type=styles&index=0!./favorite_button.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./favorite_button.js\"\nimport __vue_script__ from \"!!babel-loader!./favorite_button.js\"/* template */\nimport {render as __vue_render__, staticRenderFns as __vue_static_render_fns__} from \"!!../../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-2ced002f\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./favorite_button.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nimport normalizeComponent from \"!../../../node_modules/vue-loader/lib/runtime/component-normalizer\"\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_render__,\n __vue_static_render_fns__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return (_vm.loggedIn)?_c('div',[_c('i',{staticClass:\"button-icon favorite-button fav-active\",class:_vm.classes,attrs:{\"title\":_vm.$t('tool_tip.favorite')},on:{\"click\":function($event){$event.preventDefault();_vm.favorite()}}}),_vm._v(\" \"),(!_vm.mergedConfig.hidePostStats && _vm.status.fave_num > 0)?_c('span',[_vm._v(_vm._s(_vm.status.fave_num))]):_vm._e()]):_c('div',[_c('i',{staticClass:\"button-icon favorite-button\",class:_vm.classes,attrs:{\"title\":_vm.$t('tool_tip.favorite')}}),_vm._v(\" \"),(!_vm.mergedConfig.hidePostStats && _vm.status.fave_num > 0)?_c('span',[_vm._v(_vm._s(_vm.status.fave_num))]):_vm._e()])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","import Popover from '../popover/popover.vue'\nimport { mapGetters } from 'vuex'\n\nconst ReactButton = {\n props: ['status', 'loggedIn'],\n data () {\n return {\n filterWord: ''\n }\n },\n components: {\n Popover\n },\n methods: {\n addReaction (event, emoji, close) {\n const existingReaction = this.status.emoji_reactions.find(r => r.name === emoji)\n if (existingReaction && existingReaction.me) {\n this.$store.dispatch('unreactWithEmoji', { id: this.status.id, emoji })\n } else {\n this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji })\n }\n close()\n }\n },\n computed: {\n commonEmojis () {\n return ['❤️', '😠', '👀', '😂', '🔥']\n },\n emojis () {\n if (this.filterWord !== '') {\n return this.$store.state.instance.emoji.filter(emoji => emoji.displayText.includes(this.filterWord))\n }\n return this.$store.state.instance.emoji || []\n },\n ...mapGetters(['mergedConfig'])\n }\n}\n\nexport default ReactButton\n","function injectStyle (context) {\n require(\"!!vue-style-loader!css-loader?minimize!../../../node_modules/vue-loader/lib/style-compiler/index?{\\\"optionsId\\\":\\\"0\\\",\\\"vue\\\":true,\\\"scoped\\\":false,\\\"sourceMap\\\":false}!sass-loader!../../../node_modules/vue-loader/lib/selector?type=styles&index=0!./react_button.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./react_button.js\"\nimport __vue_script__ from \"!!babel-loader!./react_button.js\"/* template */\nimport {render as __vue_render__, staticRenderFns as __vue_static_render_fns__} from \"!!../../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-8ce5d61a\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./react_button.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nimport normalizeComponent from \"!../../../node_modules/vue-loader/lib/runtime/component-normalizer\"\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_render__,\n __vue_static_render_fns__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('Popover',{staticClass:\"react-button-popover\",attrs:{\"trigger\":\"click\",\"placement\":\"top\",\"offset\":{ y: 5 }},scopedSlots:_vm._u([{key:\"content\",fn:function(ref){\nvar close = ref.close;\nreturn _c('div',{},[_c('div',{staticClass:\"reaction-picker-filter\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.filterWord),expression:\"filterWord\"}],attrs:{\"placeholder\":_vm.$t('emoji.search_emoji')},domProps:{\"value\":(_vm.filterWord)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.filterWord=$event.target.value}}})]),_vm._v(\" \"),_c('div',{staticClass:\"reaction-picker\"},[_vm._l((_vm.commonEmojis),function(emoji){return _c('span',{key:emoji,staticClass:\"emoji-button\",on:{\"click\":function($event){_vm.addReaction($event, emoji, close)}}},[_vm._v(\"\\n \"+_vm._s(emoji)+\"\\n \")])}),_vm._v(\" \"),_c('div',{staticClass:\"reaction-picker-divider\"}),_vm._v(\" \"),_vm._l((_vm.emojis),function(emoji,key){return _c('span',{key:key,staticClass:\"emoji-button\",on:{\"click\":function($event){_vm.addReaction($event, emoji.replacement, close)}}},[_vm._v(\"\\n \"+_vm._s(emoji.replacement)+\"\\n \")])}),_vm._v(\" \"),_c('div',{staticClass:\"reaction-bottom-fader\"})],2)])}}])},[(_vm.loggedIn)?_c('i',{staticClass:\"icon-smile button-icon add-reaction-button\",attrs:{\"slot\":\"trigger\",\"title\":_vm.$t('tool_tip.add_reaction')},slot:\"trigger\"}):_vm._e()])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","import { mapGetters } from 'vuex'\n\nconst RetweetButton = {\n props: ['status', 'loggedIn', 'visibility'],\n data () {\n return {\n animated: false\n }\n },\n methods: {\n retweet () {\n if (!this.status.repeated) {\n this.$store.dispatch('retweet', { id: this.status.id })\n } else {\n this.$store.dispatch('unretweet', { id: this.status.id })\n }\n this.animated = true\n setTimeout(() => {\n this.animated = false\n }, 500)\n }\n },\n computed: {\n classes () {\n return {\n 'retweeted': this.status.repeated,\n 'retweeted-empty': !this.status.repeated,\n 'animate-spin': this.animated\n }\n },\n ...mapGetters(['mergedConfig'])\n }\n}\n\nexport default RetweetButton\n","function injectStyle (context) {\n require(\"!!vue-style-loader!css-loader?minimize!../../../node_modules/vue-loader/lib/style-compiler/index?{\\\"optionsId\\\":\\\"0\\\",\\\"vue\\\":true,\\\"scoped\\\":false,\\\"sourceMap\\\":false}!sass-loader!../../../node_modules/vue-loader/lib/selector?type=styles&index=0!./retweet_button.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./retweet_button.js\"\nimport __vue_script__ from \"!!babel-loader!./retweet_button.js\"/* template */\nimport {render as __vue_render__, staticRenderFns as __vue_static_render_fns__} from \"!!../../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-538410cc\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./retweet_button.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nimport normalizeComponent from \"!../../../node_modules/vue-loader/lib/runtime/component-normalizer\"\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_render__,\n __vue_static_render_fns__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return (_vm.loggedIn)?_c('div',[(_vm.visibility !== 'private' && _vm.visibility !== 'direct')?[_c('i',{staticClass:\"button-icon retweet-button icon-retweet rt-active\",class:_vm.classes,attrs:{\"title\":_vm.$t('tool_tip.repeat')},on:{\"click\":function($event){$event.preventDefault();_vm.retweet()}}}),_vm._v(\" \"),(!_vm.mergedConfig.hidePostStats && _vm.status.repeat_num > 0)?_c('span',[_vm._v(_vm._s(_vm.status.repeat_num))]):_vm._e()]:[_c('i',{staticClass:\"button-icon icon-lock\",class:_vm.classes,attrs:{\"title\":_vm.$t('timeline.no_retweet_hint')}})]],2):(!_vm.loggedIn)?_c('div',[_c('i',{staticClass:\"button-icon icon-retweet\",class:_vm.classes,attrs:{\"title\":_vm.$t('tool_tip.repeat')}}),_vm._v(\" \"),(!_vm.mergedConfig.hidePostStats && _vm.status.repeat_num > 0)?_c('span',[_vm._v(_vm._s(_vm.status.repeat_num))]):_vm._e()]):_vm._e()}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","import Timeago from '../timeago/timeago.vue'\nimport { forEach, map } from 'lodash'\n\nexport default {\n name: 'Poll',\n props: ['basePoll'],\n components: { Timeago },\n data () {\n return {\n loading: false,\n choices: []\n }\n },\n created () {\n if (!this.$store.state.polls.pollsObject[this.pollId]) {\n this.$store.dispatch('mergeOrAddPoll', this.basePoll)\n }\n this.$store.dispatch('trackPoll', this.pollId)\n },\n destroyed () {\n this.$store.dispatch('untrackPoll', this.pollId)\n },\n computed: {\n pollId () {\n return this.basePoll.id\n },\n poll () {\n const storePoll = this.$store.state.polls.pollsObject[this.pollId]\n return storePoll || {}\n },\n options () {\n return (this.poll && this.poll.options) || []\n },\n expiresAt () {\n return (this.poll && this.poll.expires_at) || 0\n },\n expired () {\n return (this.poll && this.poll.expired) || false\n },\n loggedIn () {\n return this.$store.state.users.currentUser\n },\n showResults () {\n return this.poll.voted || this.expired || !this.loggedIn\n },\n totalVotesCount () {\n return this.poll.votes_count\n },\n containerClass () {\n return {\n loading: this.loading\n }\n },\n choiceIndices () {\n // Convert array of booleans into an array of indices of the\n // items that were 'true', so [true, false, false, true] becomes\n // [0, 3].\n return this.choices\n .map((entry, index) => entry && index)\n .filter(value => typeof value === 'number')\n },\n isDisabled () {\n const noChoice = this.choiceIndices.length === 0\n return this.loading || noChoice\n }\n },\n methods: {\n percentageForOption (count) {\n return this.totalVotesCount === 0 ? 0 : Math.round(count / this.totalVotesCount * 100)\n },\n resultTitle (option) {\n return `${option.votes_count}/${this.totalVotesCount} ${this.$t('polls.votes')}`\n },\n fetchPoll () {\n this.$store.dispatch('refreshPoll', { id: this.statusId, pollId: this.poll.id })\n },\n activateOption (index) {\n // forgive me father: doing checking the radio/checkboxes\n // in code because of customized input elements need either\n // a) an extra element for the actual graphic, or b) use a\n // pseudo element for the label. We use b) which mandates\n // using \"for\" and \"id\" matching which isn't nice when the\n // same poll appears multiple times on the site (notifs and\n // timeline for example). With code we can make sure it just\n // works without altering the pseudo element implementation.\n const allElements = this.$el.querySelectorAll('input')\n const clickedElement = this.$el.querySelector(`input[value=\"${index}\"]`)\n if (this.poll.multiple) {\n // Checkboxes, toggle only the clicked one\n clickedElement.checked = !clickedElement.checked\n } else {\n // Radio button, uncheck everything and check the clicked one\n forEach(allElements, element => { element.checked = false })\n clickedElement.checked = true\n }\n this.choices = map(allElements, e => e.checked)\n },\n optionId (index) {\n return `poll${this.poll.id}-${index}`\n },\n vote () {\n if (this.choiceIndices.length === 0) return\n this.loading = true\n this.$store.dispatch(\n 'votePoll',\n { id: this.statusId, pollId: this.poll.id, choices: this.choiceIndices }\n ).then(poll => {\n this.loading = false\n })\n }\n }\n}\n","function injectStyle (context) {\n require(\"!!vue-style-loader!css-loader?minimize!../../../node_modules/vue-loader/lib/style-compiler/index?{\\\"optionsId\\\":\\\"0\\\",\\\"vue\\\":true,\\\"scoped\\\":false,\\\"sourceMap\\\":false}!sass-loader!../../../node_modules/vue-loader/lib/selector?type=styles&index=0!./poll.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./poll.js\"\nimport __vue_script__ from \"!!babel-loader!./poll.js\"/* template */\nimport {render as __vue_render__, staticRenderFns as __vue_static_render_fns__} from \"!!../../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-db51c57e\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./poll.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nimport normalizeComponent from \"!../../../node_modules/vue-loader/lib/runtime/component-normalizer\"\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_render__,\n __vue_static_render_fns__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"poll\",class:_vm.containerClass},[_vm._l((_vm.options),function(option,index){return _c('div',{key:index,staticClass:\"poll-option\"},[(_vm.showResults)?_c('div',{staticClass:\"option-result\",attrs:{\"title\":_vm.resultTitle(option)}},[_c('div',{staticClass:\"option-result-label\"},[_c('span',{staticClass:\"result-percentage\"},[_vm._v(\"\\n \"+_vm._s(_vm.percentageForOption(option.votes_count))+\"%\\n \")]),_vm._v(\" \"),_c('span',[_vm._v(_vm._s(option.title))])]),_vm._v(\" \"),_c('div',{staticClass:\"result-fill\",style:({ 'width': ((_vm.percentageForOption(option.votes_count)) + \"%\") })})]):_c('div',{on:{\"click\":function($event){_vm.activateOption(index)}}},[(_vm.poll.multiple)?_c('input',{attrs:{\"type\":\"checkbox\",\"disabled\":_vm.loading},domProps:{\"value\":index}}):_c('input',{attrs:{\"type\":\"radio\",\"disabled\":_vm.loading},domProps:{\"value\":index}}),_vm._v(\" \"),_c('label',{staticClass:\"option-vote\"},[_c('div',[_vm._v(_vm._s(option.title))])])])])}),_vm._v(\" \"),_c('div',{staticClass:\"footer faint\"},[(!_vm.showResults)?_c('button',{staticClass:\"btn btn-default poll-vote-button\",attrs:{\"type\":\"button\",\"disabled\":_vm.isDisabled},on:{\"click\":_vm.vote}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('polls.vote'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),_c('div',{staticClass:\"total\"},[_vm._v(\"\\n \"+_vm._s(_vm.totalVotesCount)+\" \"+_vm._s(_vm.$t(\"polls.votes\"))+\" · \\n \")]),_vm._v(\" \"),_c('i18n',{attrs:{\"path\":_vm.expired ? 'polls.expired' : 'polls.expires_in'}},[_c('Timeago',{attrs:{\"time\":_vm.expiresAt,\"auto-update\":60,\"now-threshold\":0}})],1)],1)],2)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","import Popover from '../popover/popover.vue'\n\nconst ExtraButtons = {\n props: [ 'status' ],\n components: { Popover },\n methods: {\n deleteStatus () {\n const confirmed = window.confirm(this.$t('status.delete_confirm'))\n if (confirmed) {\n this.$store.dispatch('deleteStatus', { id: this.status.id })\n }\n },\n pinStatus () {\n this.$store.dispatch('pinStatus', this.status.id)\n .then(() => this.$emit('onSuccess'))\n .catch(err => this.$emit('onError', err.error.error))\n },\n unpinStatus () {\n this.$store.dispatch('unpinStatus', this.status.id)\n .then(() => this.$emit('onSuccess'))\n .catch(err => this.$emit('onError', err.error.error))\n },\n muteConversation () {\n this.$store.dispatch('muteConversation', this.status.id)\n .then(() => this.$emit('onSuccess'))\n .catch(err => this.$emit('onError', err.error.error))\n },\n unmuteConversation () {\n this.$store.dispatch('unmuteConversation', this.status.id)\n .then(() => this.$emit('onSuccess'))\n .catch(err => this.$emit('onError', err.error.error))\n }\n },\n computed: {\n currentUser () { return this.$store.state.users.currentUser },\n canDelete () {\n if (!this.currentUser) { return }\n const superuser = this.currentUser.rights.moderator || this.currentUser.rights.admin\n return superuser || this.status.user.id === this.currentUser.id\n },\n ownStatus () {\n return this.status.user.id === this.currentUser.id\n },\n canPin () {\n return this.ownStatus && (this.status.visibility === 'public' || this.status.visibility === 'unlisted')\n },\n canMute () {\n return !!this.currentUser\n }\n }\n}\n\nexport default ExtraButtons\n","function injectStyle (context) {\n require(\"!!vue-style-loader!css-loader?minimize!../../../node_modules/vue-loader/lib/style-compiler/index?{\\\"optionsId\\\":\\\"0\\\",\\\"vue\\\":true,\\\"scoped\\\":false,\\\"sourceMap\\\":false}!sass-loader!../../../node_modules/vue-loader/lib/selector?type=styles&index=0!./extra_buttons.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./extra_buttons.js\"\nimport __vue_script__ from \"!!babel-loader!./extra_buttons.js\"/* template */\nimport {render as __vue_render__, staticRenderFns as __vue_static_render_fns__} from \"!!../../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-0551c732\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./extra_buttons.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nimport normalizeComponent from \"!../../../node_modules/vue-loader/lib/runtime/component-normalizer\"\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_render__,\n __vue_static_render_fns__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return (_vm.canDelete || _vm.canMute || _vm.canPin)?_c('Popover',{staticClass:\"extra-button-popover\",attrs:{\"trigger\":\"click\",\"placement\":\"top\"}},[_c('div',{attrs:{\"slot\":\"content\"},slot:\"content\"},[_c('div',{staticClass:\"dropdown-menu\"},[(_vm.canMute && !_vm.status.thread_muted)?_c('button',{staticClass:\"dropdown-item dropdown-item-icon\",on:{\"click\":function($event){$event.preventDefault();return _vm.muteConversation($event)}}},[_c('i',{staticClass:\"icon-eye-off\"}),_c('span',[_vm._v(_vm._s(_vm.$t(\"status.mute_conversation\")))])]):_vm._e(),_vm._v(\" \"),(_vm.canMute && _vm.status.thread_muted)?_c('button',{staticClass:\"dropdown-item dropdown-item-icon\",on:{\"click\":function($event){$event.preventDefault();return _vm.unmuteConversation($event)}}},[_c('i',{staticClass:\"icon-eye-off\"}),_c('span',[_vm._v(_vm._s(_vm.$t(\"status.unmute_conversation\")))])]):_vm._e(),_vm._v(\" \"),(!_vm.status.pinned && _vm.canPin)?_c('button',{directives:[{name:\"close-popover\",rawName:\"v-close-popover\"}],staticClass:\"dropdown-item dropdown-item-icon\",on:{\"click\":function($event){$event.preventDefault();return _vm.pinStatus($event)}}},[_c('i',{staticClass:\"icon-pin\"}),_c('span',[_vm._v(_vm._s(_vm.$t(\"status.pin\")))])]):_vm._e(),_vm._v(\" \"),(_vm.status.pinned && _vm.canPin)?_c('button',{directives:[{name:\"close-popover\",rawName:\"v-close-popover\"}],staticClass:\"dropdown-item dropdown-item-icon\",on:{\"click\":function($event){$event.preventDefault();return _vm.unpinStatus($event)}}},[_c('i',{staticClass:\"icon-pin\"}),_c('span',[_vm._v(_vm._s(_vm.$t(\"status.unpin\")))])]):_vm._e(),_vm._v(\" \"),(_vm.canDelete)?_c('button',{directives:[{name:\"close-popover\",rawName:\"v-close-popover\"}],staticClass:\"dropdown-item dropdown-item-icon\",on:{\"click\":function($event){$event.preventDefault();return _vm.deleteStatus($event)}}},[_c('i',{staticClass:\"icon-cancel\"}),_c('span',[_vm._v(_vm._s(_vm.$t(\"status.delete\")))])]):_vm._e()])]),_vm._v(\" \"),_c('i',{staticClass:\"icon-ellipsis button-icon\",attrs:{\"slot\":\"trigger\"},slot:\"trigger\"})]):_vm._e()}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","import Attachment from '../attachment/attachment.vue'\nimport { chunk, last, dropRight, sumBy } from 'lodash'\n\nconst Gallery = {\n props: [\n 'attachments',\n 'nsfw',\n 'setMedia'\n ],\n data () {\n return {\n sizes: {}\n }\n },\n components: { Attachment },\n computed: {\n rows () {\n if (!this.attachments) {\n return []\n }\n const rows = chunk(this.attachments, 3)\n if (last(rows).length === 1 && rows.length > 1) {\n // if 1 attachment on last row -> add it to the previous row instead\n const lastAttachment = last(rows)[0]\n const allButLastRow = dropRight(rows)\n last(allButLastRow).push(lastAttachment)\n return allButLastRow\n }\n return rows\n },\n useContainFit () {\n return this.$store.getters.mergedConfig.useContainFit\n }\n },\n methods: {\n onNaturalSizeLoad (id, size) {\n this.$set(this.sizes, id, size)\n },\n rowStyle (itemsPerRow) {\n return { 'padding-bottom': `${(100 / (itemsPerRow + 0.6))}%` }\n },\n itemStyle (id, row) {\n const total = sumBy(row, item => this.getAspectRatio(item.id))\n return { flex: `${this.getAspectRatio(id) / total} 1 0%` }\n },\n getAspectRatio (id) {\n const size = this.sizes[id]\n return size ? size.width / size.height : 1\n }\n }\n}\n\nexport default Gallery\n","function injectStyle (context) {\n require(\"!!vue-style-loader!css-loader?minimize!../../../node_modules/vue-loader/lib/style-compiler/index?{\\\"optionsId\\\":\\\"0\\\",\\\"vue\\\":true,\\\"scoped\\\":false,\\\"sourceMap\\\":false}!sass-loader!../../../node_modules/vue-loader/lib/selector?type=styles&index=0!./gallery.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./gallery.js\"\nimport __vue_script__ from \"!!babel-loader!./gallery.js\"/* template */\nimport {render as __vue_render__, staticRenderFns as __vue_static_render_fns__} from \"!!../../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-68a574b8\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./gallery.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nimport normalizeComponent from \"!../../../node_modules/vue-loader/lib/runtime/component-normalizer\"\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_render__,\n __vue_static_render_fns__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{ref:\"galleryContainer\",staticStyle:{\"width\":\"100%\"}},_vm._l((_vm.rows),function(row,index){return _c('div',{key:index,staticClass:\"gallery-row\",class:{ 'contain-fit': _vm.useContainFit, 'cover-fit': !_vm.useContainFit },style:(_vm.rowStyle(row.length))},[_c('div',{staticClass:\"gallery-row-inner\"},_vm._l((row),function(attachment){return _c('attachment',{key:attachment.id,style:(_vm.itemStyle(attachment.id, row)),attrs:{\"set-media\":_vm.setMedia,\"nsfw\":_vm.nsfw,\"attachment\":attachment,\"allow-play\":false,\"natural-size-load\":_vm.onNaturalSizeLoad.bind(null, attachment.id)}})}),1)])}),0)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","const LinkPreview = {\n name: 'LinkPreview',\n props: [\n 'card',\n 'size',\n 'nsfw'\n ],\n data () {\n return {\n imageLoaded: false\n }\n },\n computed: {\n useImage () {\n // Currently BE shoudn't give cards if tagged NSFW, this is a bit paranoid\n // as it makes sure to hide the image if somehow NSFW tagged preview can\n // exist.\n return this.card.image && !this.nsfw && this.size !== 'hide'\n },\n useDescription () {\n return this.card.description && /\\S/.test(this.card.description)\n }\n },\n created () {\n if (this.useImage) {\n const newImg = new Image()\n newImg.onload = () => {\n this.imageLoaded = true\n }\n newImg.src = this.card.image\n }\n }\n}\n\nexport default LinkPreview\n","function injectStyle (context) {\n require(\"!!vue-style-loader!css-loader?minimize!../../../node_modules/vue-loader/lib/style-compiler/index?{\\\"optionsId\\\":\\\"0\\\",\\\"vue\\\":true,\\\"scoped\\\":false,\\\"sourceMap\\\":false}!sass-loader!../../../node_modules/vue-loader/lib/selector?type=styles&index=0!./link-preview.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./link-preview.js\"\nimport __vue_script__ from \"!!babel-loader!./link-preview.js\"/* template */\nimport {render as __vue_render__, staticRenderFns as __vue_static_render_fns__} from \"!!../../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-7c8d99ac\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./link-preview.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nimport normalizeComponent from \"!../../../node_modules/vue-loader/lib/runtime/component-normalizer\"\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_render__,\n __vue_static_render_fns__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('a',{staticClass:\"link-preview-card\",attrs:{\"href\":_vm.card.url,\"target\":\"_blank\",\"rel\":\"noopener\"}},[(_vm.useImage && _vm.imageLoaded)?_c('div',{staticClass:\"card-image\",class:{ 'small-image': _vm.size === 'small' }},[_c('img',{attrs:{\"src\":_vm.card.image}})]):_vm._e(),_vm._v(\" \"),_c('div',{staticClass:\"card-content\"},[_c('span',{staticClass:\"card-host faint\"},[_vm._v(_vm._s(_vm.card.provider_name))]),_vm._v(\" \"),_c('h4',{staticClass:\"card-title\"},[_vm._v(_vm._s(_vm.card.title))]),_vm._v(\" \"),(_vm.useDescription)?_c('p',{staticClass:\"card-description\"},[_vm._v(_vm._s(_vm.card.description))]):_vm._e()])])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","import UserAvatar from '../user_avatar/user_avatar.vue'\nimport generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'\n\nconst AvatarList = {\n props: ['users'],\n computed: {\n slicedUsers () {\n return this.users ? this.users.slice(0, 15) : []\n }\n },\n components: {\n UserAvatar\n },\n methods: {\n userProfileLink (user) {\n return generateProfileLink(user.id, user.screen_name, this.$store.state.instance.restrictedNicknames)\n }\n }\n}\n\nexport default AvatarList\n","function injectStyle (context) {\n require(\"!!vue-style-loader!css-loader?minimize!../../../node_modules/vue-loader/lib/style-compiler/index?{\\\"optionsId\\\":\\\"0\\\",\\\"vue\\\":true,\\\"scoped\\\":false,\\\"sourceMap\\\":false}!sass-loader!../../../node_modules/vue-loader/lib/selector?type=styles&index=0!./avatar_list.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./avatar_list.js\"\nimport __vue_script__ from \"!!babel-loader!./avatar_list.js\"/* template */\nimport {render as __vue_render__, staticRenderFns as __vue_static_render_fns__} from \"!!../../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-4cea5bcf\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./avatar_list.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nimport normalizeComponent from \"!../../../node_modules/vue-loader/lib/runtime/component-normalizer\"\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_render__,\n __vue_static_render_fns__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"avatars\"},_vm._l((_vm.slicedUsers),function(user){return _c('router-link',{key:user.id,staticClass:\"avatars-item\",attrs:{\"to\":_vm.userProfileLink(user)}},[_c('UserAvatar',{staticClass:\"avatar-small\",attrs:{\"user\":user}})],1)}),1)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","import { find } from 'lodash'\n\nconst StatusPopover = {\n name: 'StatusPopover',\n props: [\n 'statusId'\n ],\n data () {\n return {\n error: false\n }\n },\n computed: {\n status () {\n return find(this.$store.state.statuses.allStatuses, { id: this.statusId })\n }\n },\n components: {\n Status: () => import('../status/status.vue'),\n Popover: () => import('../popover/popover.vue')\n },\n methods: {\n enter () {\n if (!this.status) {\n this.$store.dispatch('fetchStatus', this.statusId)\n .then(data => (this.error = false))\n .catch(e => (this.error = true))\n }\n }\n }\n}\n\nexport default StatusPopover\n","function injectStyle (context) {\n require(\"!!vue-style-loader!css-loader?minimize!../../../node_modules/vue-loader/lib/style-compiler/index?{\\\"optionsId\\\":\\\"0\\\",\\\"vue\\\":true,\\\"scoped\\\":false,\\\"sourceMap\\\":false}!sass-loader!../../../node_modules/vue-loader/lib/selector?type=styles&index=0!./status_popover.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./status_popover.js\"\nimport __vue_script__ from \"!!babel-loader!./status_popover.js\"/* template */\nimport {render as __vue_render__, staticRenderFns as __vue_static_render_fns__} from \"!!../../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-3b873076\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./status_popover.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nimport normalizeComponent from \"!../../../node_modules/vue-loader/lib/runtime/component-normalizer\"\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_render__,\n __vue_static_render_fns__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('Popover',{attrs:{\"trigger\":\"hover\",\"popover-class\":\"status-popover\",\"bound-to\":{ x: 'container' }},on:{\"show\":_vm.enter}},[_c('template',{slot:\"trigger\"},[_vm._t(\"default\")],2),_vm._v(\" \"),_c('div',{attrs:{\"slot\":\"content\"},slot:\"content\"},[(_vm.status)?_c('Status',{attrs:{\"is-preview\":true,\"statusoid\":_vm.status,\"compact\":true}}):(_vm.error)?_c('div',{staticClass:\"status-preview-no-content faint\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('status.status_unavailable'))+\"\\n \")]):_c('div',{staticClass:\"status-preview-no-content\"},[_c('i',{staticClass:\"icon-spin4 animate-spin\"})])],1)],2)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","import UserAvatar from '../user_avatar/user_avatar.vue'\nimport Popover from '../popover/popover.vue'\n\nconst EMOJI_REACTION_COUNT_CUTOFF = 12\n\nconst EmojiReactions = {\n name: 'EmojiReactions',\n components: {\n UserAvatar,\n Popover\n },\n props: ['status'],\n data: () => ({\n showAll: false\n }),\n computed: {\n tooManyReactions () {\n return this.status.emoji_reactions.length > EMOJI_REACTION_COUNT_CUTOFF\n },\n emojiReactions () {\n return this.showAll\n ? this.status.emoji_reactions\n : this.status.emoji_reactions.slice(0, EMOJI_REACTION_COUNT_CUTOFF)\n },\n showMoreString () {\n return `+${this.status.emoji_reactions.length - EMOJI_REACTION_COUNT_CUTOFF}`\n },\n accountsForEmoji () {\n return this.status.emoji_reactions.reduce((acc, reaction) => {\n acc[reaction.name] = reaction.accounts || []\n return acc\n }, {})\n },\n loggedIn () {\n return !!this.$store.state.users.currentUser\n }\n },\n methods: {\n toggleShowAll () {\n this.showAll = !this.showAll\n },\n reactedWith (emoji) {\n return this.status.emoji_reactions.find(r => r.name === emoji).me\n },\n fetchEmojiReactionsByIfMissing () {\n const hasNoAccounts = this.status.emoji_reactions.find(r => !r.accounts)\n if (hasNoAccounts) {\n this.$store.dispatch('fetchEmojiReactionsBy', this.status.id)\n }\n },\n reactWith (emoji) {\n this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji })\n },\n unreact (emoji) {\n this.$store.dispatch('unreactWithEmoji', { id: this.status.id, emoji })\n },\n emojiOnClick (emoji, event) {\n if (!this.loggedIn) return\n\n if (this.reactedWith(emoji)) {\n this.unreact(emoji)\n } else {\n this.reactWith(emoji)\n }\n }\n }\n}\n\nexport default EmojiReactions\n","function injectStyle (context) {\n require(\"!!vue-style-loader!css-loader?minimize!../../../node_modules/vue-loader/lib/style-compiler/index?{\\\"optionsId\\\":\\\"0\\\",\\\"vue\\\":true,\\\"scoped\\\":false,\\\"sourceMap\\\":false}!sass-loader!../../../node_modules/vue-loader/lib/selector?type=styles&index=0!./emoji_reactions.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./emoji_reactions.js\"\nimport __vue_script__ from \"!!babel-loader!./emoji_reactions.js\"/* template */\nimport {render as __vue_render__, staticRenderFns as __vue_static_render_fns__} from \"!!../../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-09ec7fb6\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./emoji_reactions.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nimport normalizeComponent from \"!../../../node_modules/vue-loader/lib/runtime/component-normalizer\"\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_render__,\n __vue_static_render_fns__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"emoji-reactions\"},[_vm._l((_vm.emojiReactions),function(reaction){return _c('Popover',{key:reaction.name,attrs:{\"trigger\":\"hover\",\"placement\":\"top\",\"offset\":{ y: 5 }}},[_c('div',{staticClass:\"reacted-users\",attrs:{\"slot\":\"content\"},slot:\"content\"},[(_vm.accountsForEmoji[reaction.name].length)?_c('div',_vm._l((_vm.accountsForEmoji[reaction.name]),function(account){return _c('div',{key:account.id,staticClass:\"reacted-user\"},[_c('UserAvatar',{staticClass:\"avatar-small\",attrs:{\"user\":account,\"compact\":true}}),_vm._v(\" \"),_c('div',{staticClass:\"reacted-user-names\"},[_c('span',{staticClass:\"reacted-user-name\",domProps:{\"innerHTML\":_vm._s(account.name_html)}}),_vm._v(\" \"),_c('span',{staticClass:\"reacted-user-screen-name\"},[_vm._v(_vm._s(account.screen_name))])])],1)}),0):_c('div',[_c('i',{staticClass:\"icon-spin4 animate-spin\"})])]),_vm._v(\" \"),_c('button',{staticClass:\"emoji-reaction btn btn-default\",class:{ 'picked-reaction': _vm.reactedWith(reaction.name), 'not-clickable': !_vm.loggedIn },attrs:{\"slot\":\"trigger\"},on:{\"click\":function($event){_vm.emojiOnClick(reaction.name, $event)},\"mouseenter\":function($event){_vm.fetchEmojiReactionsByIfMissing()}},slot:\"trigger\"},[_c('span',{staticClass:\"reaction-emoji\"},[_vm._v(_vm._s(reaction.name))]),_vm._v(\" \"),_c('span',[_vm._v(_vm._s(reaction.count))])])])}),_vm._v(\" \"),(_vm.tooManyReactions)?_c('a',{staticClass:\"emoji-reaction-expand faint\",attrs:{\"href\":\"javascript:void(0)\"},on:{\"click\":_vm.toggleShowAll}},[_vm._v(\"\\n \"+_vm._s(_vm.showAll ? _vm.$t('general.show_less') : _vm.showMoreString)+\"\\n \")]):_vm._e()],2)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","import Attachment from '../attachment/attachment.vue'\nimport FavoriteButton from '../favorite_button/favorite_button.vue'\nimport ReactButton from '../react_button/react_button.vue'\nimport RetweetButton from '../retweet_button/retweet_button.vue'\nimport Poll from '../poll/poll.vue'\nimport ExtraButtons from '../extra_buttons/extra_buttons.vue'\nimport PostStatusForm from '../post_status_form/post_status_form.vue'\nimport UserCard from '../user_card/user_card.vue'\nimport UserAvatar from '../user_avatar/user_avatar.vue'\nimport Gallery from '../gallery/gallery.vue'\nimport LinkPreview from '../link-preview/link-preview.vue'\nimport AvatarList from '../avatar_list/avatar_list.vue'\nimport Timeago from '../timeago/timeago.vue'\nimport StatusPopover from '../status_popover/status_popover.vue'\nimport EmojiReactions from '../emoji_reactions/emoji_reactions.vue'\nimport generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'\nimport fileType from 'src/services/file_type/file_type.service'\nimport { processHtml } from 'src/services/tiny_post_html_processor/tiny_post_html_processor.service.js'\nimport { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'\nimport { mentionMatchesUrl, extractTagFromUrl } from 'src/services/matcher/matcher.service.js'\nimport { filter, unescape, uniqBy } from 'lodash'\nimport { mapGetters, mapState } from 'vuex'\n\nconst Status = {\n name: 'Status',\n props: [\n 'statusoid',\n 'expandable',\n 'inConversation',\n 'focused',\n 'highlight',\n 'compact',\n 'replies',\n 'isPreview',\n 'noHeading',\n 'inlineExpanded',\n 'showPinned',\n 'inProfile',\n 'profileUserId'\n ],\n data () {\n return {\n replying: false,\n unmuted: false,\n userExpanded: false,\n showingTall: this.inConversation && this.focused,\n showingLongSubject: false,\n error: null,\n // not as computed because it sets the initial state which will be changed later\n expandingSubject: !this.$store.getters.mergedConfig.collapseMessageWithSubject\n }\n },\n computed: {\n localCollapseSubjectDefault () {\n return this.mergedConfig.collapseMessageWithSubject\n },\n muteWords () {\n return this.mergedConfig.muteWords\n },\n repeaterClass () {\n const user = this.statusoid.user\n return highlightClass(user)\n },\n userClass () {\n const user = this.retweet ? (this.statusoid.retweeted_status.user) : this.statusoid.user\n return highlightClass(user)\n },\n deleted () {\n return this.statusoid.deleted\n },\n repeaterStyle () {\n const user = this.statusoid.user\n const highlight = this.mergedConfig.highlight\n return highlightStyle(highlight[user.screen_name])\n },\n userStyle () {\n if (this.noHeading) return\n const user = this.retweet ? (this.statusoid.retweeted_status.user) : this.statusoid.user\n const highlight = this.mergedConfig.highlight\n return highlightStyle(highlight[user.screen_name])\n },\n hideAttachments () {\n return (this.mergedConfig.hideAttachments && !this.inConversation) ||\n (this.mergedConfig.hideAttachmentsInConv && this.inConversation)\n },\n userProfileLink () {\n return this.generateUserProfileLink(this.status.user.id, this.status.user.screen_name)\n },\n replyProfileLink () {\n if (this.isReply) {\n return this.generateUserProfileLink(this.status.in_reply_to_user_id, this.replyToName)\n }\n },\n retweet () { return !!this.statusoid.retweeted_status },\n retweeter () { return this.statusoid.user.name || this.statusoid.user.screen_name },\n retweeterHtml () { return this.statusoid.user.name_html },\n retweeterProfileLink () { return this.generateUserProfileLink(this.statusoid.user.id, this.statusoid.user.screen_name) },\n status () {\n if (this.retweet) {\n return this.statusoid.retweeted_status\n } else {\n return this.statusoid\n }\n },\n statusFromGlobalRepository () {\n // NOTE: Consider to replace status with statusFromGlobalRepository\n return this.$store.state.statuses.allStatusesObject[this.status.id]\n },\n loggedIn () {\n return !!this.currentUser\n },\n muteWordHits () {\n const statusText = this.status.text.toLowerCase()\n const statusSummary = this.status.summary.toLowerCase()\n const hits = filter(this.muteWords, (muteWord) => {\n return statusText.includes(muteWord.toLowerCase()) || statusSummary.includes(muteWord.toLowerCase())\n })\n\n return hits\n },\n muted () { return !this.unmuted && ((!(this.inProfile && this.status.user.id === this.profileUserId) && this.status.user.muted) || (!this.inConversation && this.status.thread_muted) || this.muteWordHits.length > 0) },\n hideFilteredStatuses () {\n return this.mergedConfig.hideFilteredStatuses\n },\n hideStatus () {\n return (this.hideReply || this.deleted) || (this.muted && this.hideFilteredStatuses)\n },\n isFocused () {\n // retweet or root of an expanded conversation\n if (this.focused) {\n return true\n } else if (!this.inConversation) {\n return false\n }\n // use conversation highlight only when in conversation\n return this.status.id === this.highlight\n },\n // This is a bit hacky, but we want to approximate post height before rendering\n // so we count newlines (masto uses

for paragraphs, GS uses
between them)\n // as well as approximate line count by counting characters and approximating ~80\n // per line.\n //\n // Using max-height + overflow: auto for status components resulted in false positives\n // very often with japanese characters, and it was very annoying.\n tallStatus () {\n const lengthScore = this.status.statusnet_html.split(/ 20\n },\n longSubject () {\n return this.status.summary.length > 900\n },\n isReply () {\n return !!(this.status.in_reply_to_status_id && this.status.in_reply_to_user_id)\n },\n replyToName () {\n if (this.status.in_reply_to_screen_name) {\n return this.status.in_reply_to_screen_name\n } else {\n const user = this.$store.getters.findUser(this.status.in_reply_to_user_id)\n return user && user.screen_name\n }\n },\n hideReply () {\n if (this.mergedConfig.replyVisibility === 'all') {\n return false\n }\n if (this.inConversation || !this.isReply) {\n return false\n }\n if (this.status.user.id === this.currentUser.id) {\n return false\n }\n if (this.status.type === 'retweet') {\n return false\n }\n const checkFollowing = this.mergedConfig.replyVisibility === 'following'\n for (var i = 0; i < this.status.attentions.length; ++i) {\n if (this.status.user.id === this.status.attentions[i].id) {\n continue\n }\n const taggedUser = this.$store.getters.findUser(this.status.attentions[i].id)\n if (checkFollowing && taggedUser && taggedUser.following) {\n return false\n }\n if (this.status.attentions[i].id === this.currentUser.id) {\n return false\n }\n }\n return this.status.attentions.length > 0\n },\n\n // When a status has a subject and is also tall, we should only have one show more/less button. If the default is to collapse statuses with subjects, we just treat it like a status with a subject; otherwise, we just treat it like a tall status.\n mightHideBecauseSubject () {\n return this.status.summary && (!this.tallStatus || this.localCollapseSubjectDefault)\n },\n mightHideBecauseTall () {\n return this.tallStatus && (!this.status.summary || !this.localCollapseSubjectDefault)\n },\n hideSubjectStatus () {\n return this.mightHideBecauseSubject && !this.expandingSubject\n },\n hideTallStatus () {\n return this.mightHideBecauseTall && !this.showingTall\n },\n showingMore () {\n return (this.mightHideBecauseTall && this.showingTall) || (this.mightHideBecauseSubject && this.expandingSubject)\n },\n nsfwClickthrough () {\n if (!this.status.nsfw) {\n return false\n }\n if (this.status.summary && this.localCollapseSubjectDefault) {\n return false\n }\n return true\n },\n replySubject () {\n if (!this.status.summary) return ''\n const decodedSummary = unescape(this.status.summary)\n const behavior = this.mergedConfig.subjectLineBehavior\n const startsWithRe = decodedSummary.match(/^re[: ]/i)\n if ((behavior !== 'noop' && startsWithRe) || behavior === 'masto') {\n return decodedSummary\n } else if (behavior === 'email') {\n return 're: '.concat(decodedSummary)\n } else if (behavior === 'noop') {\n return ''\n }\n },\n attachmentSize () {\n if ((this.mergedConfig.hideAttachments && !this.inConversation) ||\n (this.mergedConfig.hideAttachmentsInConv && this.inConversation) ||\n (this.status.attachments.length > this.maxThumbnails)) {\n return 'hide'\n } else if (this.compact) {\n return 'small'\n }\n return 'normal'\n },\n galleryTypes () {\n if (this.attachmentSize === 'hide') {\n return []\n }\n return this.mergedConfig.playVideosInModal\n ? ['image', 'video']\n : ['image']\n },\n galleryAttachments () {\n return this.status.attachments.filter(\n file => fileType.fileMatchesSomeType(this.galleryTypes, file)\n )\n },\n nonGalleryAttachments () {\n return this.status.attachments.filter(\n file => !fileType.fileMatchesSomeType(this.galleryTypes, file)\n )\n },\n hasImageAttachments () {\n return this.status.attachments.some(\n file => fileType.fileType(file.mimetype) === 'image'\n )\n },\n hasVideoAttachments () {\n return this.status.attachments.some(\n file => fileType.fileType(file.mimetype) === 'video'\n )\n },\n maxThumbnails () {\n return this.mergedConfig.maxThumbnails\n },\n postBodyHtml () {\n const html = this.status.statusnet_html\n\n if (this.mergedConfig.greentext) {\n try {\n if (html.includes('>')) {\n // This checks if post has '>' at the beginning, excluding mentions so that @mention >impying works\n return processHtml(html, (string) => {\n if (string.includes('>') &&\n string\n .replace(/<[^>]+?>/gi, '') // remove all tags\n .replace(/@\\w+/gi, '') // remove mentions (even failed ones)\n .trim()\n .startsWith('>')) {\n return `${string}`\n } else {\n return string\n }\n })\n } else {\n return html\n }\n } catch (e) {\n console.err('Failed to process status html', e)\n return html\n }\n } else {\n return html\n }\n },\n contentHtml () {\n if (!this.status.summary_html) {\n return this.postBodyHtml\n }\n return this.status.summary_html + '
' + this.postBodyHtml\n },\n combinedFavsAndRepeatsUsers () {\n // Use the status from the global status repository since favs and repeats are saved in it\n const combinedUsers = [].concat(\n this.statusFromGlobalRepository.favoritedBy,\n this.statusFromGlobalRepository.rebloggedBy\n )\n return uniqBy(combinedUsers, 'id')\n },\n ownStatus () {\n return this.status.user.id === this.currentUser.id\n },\n tags () {\n return this.status.tags.filter(tagObj => tagObj.hasOwnProperty('name')).map(tagObj => tagObj.name).join(' ')\n },\n hidePostStats () {\n return this.mergedConfig.hidePostStats\n },\n ...mapGetters(['mergedConfig']),\n ...mapState({\n betterShadow: state => state.interface.browserSupport.cssFilter,\n currentUser: state => state.users.currentUser\n })\n },\n components: {\n Attachment,\n FavoriteButton,\n ReactButton,\n RetweetButton,\n ExtraButtons,\n PostStatusForm,\n Poll,\n UserCard,\n UserAvatar,\n Gallery,\n LinkPreview,\n AvatarList,\n Timeago,\n StatusPopover,\n EmojiReactions\n },\n methods: {\n visibilityIcon (visibility) {\n switch (visibility) {\n case 'private':\n return 'icon-lock'\n case 'unlisted':\n return 'icon-lock-open-alt'\n case 'direct':\n return 'icon-mail-alt'\n default:\n return 'icon-globe'\n }\n },\n showError (error) {\n this.error = error\n },\n clearError () {\n this.error = undefined\n },\n linkClicked (event) {\n const target = event.target.closest('.status-content a')\n if (target) {\n if (target.className.match(/mention/)) {\n const href = target.href\n const attn = this.status.attentions.find(attn => mentionMatchesUrl(attn, href))\n if (attn) {\n event.stopPropagation()\n event.preventDefault()\n const link = this.generateUserProfileLink(attn.id, attn.screen_name)\n this.$router.push(link)\n return\n }\n }\n if (target.rel.match(/(?:^|\\s)tag(?:$|\\s)/) || target.className.match(/hashtag/)) {\n // Extract tag name from link url\n const tag = extractTagFromUrl(target.href)\n if (tag) {\n const link = this.generateTagLink(tag)\n this.$router.push(link)\n return\n }\n }\n window.open(target.href, '_blank')\n }\n },\n toggleReplying () {\n this.replying = !this.replying\n },\n gotoOriginal (id) {\n if (this.inConversation) {\n this.$emit('goto', id)\n }\n },\n toggleExpanded () {\n this.$emit('toggleExpanded')\n },\n toggleMute () {\n this.unmuted = !this.unmuted\n },\n toggleUserExpanded () {\n this.userExpanded = !this.userExpanded\n },\n toggleShowMore () {\n if (this.mightHideBecauseTall) {\n this.showingTall = !this.showingTall\n } else if (this.mightHideBecauseSubject) {\n this.expandingSubject = !this.expandingSubject\n }\n },\n generateUserProfileLink (id, name) {\n return generateProfileLink(id, name, this.$store.state.instance.restrictedNicknames)\n },\n generateTagLink (tag) {\n return `/tag/${tag}`\n },\n setMedia () {\n const attachments = this.attachmentSize === 'hide' ? this.status.attachments : this.galleryAttachments\n return () => this.$store.dispatch('setMedia', attachments)\n }\n },\n watch: {\n 'highlight': function (id) {\n if (this.status.id === id) {\n let rect = this.$el.getBoundingClientRect()\n if (rect.top < 100) {\n // Post is above screen, match its top to screen top\n window.scrollBy(0, rect.top - 100)\n } else if (rect.height >= (window.innerHeight - 50)) {\n // Post we want to see is taller than screen so match its top to screen top\n window.scrollBy(0, rect.top - 100)\n } else if (rect.bottom > window.innerHeight - 50) {\n // Post is below screen, match its bottom to screen bottom\n window.scrollBy(0, rect.bottom - window.innerHeight + 50)\n }\n }\n },\n 'status.repeat_num': function (num) {\n // refetch repeats when repeat_num is changed in any way\n if (this.isFocused && this.statusFromGlobalRepository.rebloggedBy && this.statusFromGlobalRepository.rebloggedBy.length !== num) {\n this.$store.dispatch('fetchRepeats', this.status.id)\n }\n },\n 'status.fave_num': function (num) {\n // refetch favs when fave_num is changed in any way\n if (this.isFocused && this.statusFromGlobalRepository.favoritedBy && this.statusFromGlobalRepository.favoritedBy.length !== num) {\n this.$store.dispatch('fetchFavs', this.status.id)\n }\n }\n },\n filters: {\n capitalize: function (str) {\n return str.charAt(0).toUpperCase() + str.slice(1)\n }\n }\n}\n\nexport default Status\n","/**\n * This is a tiny purpose-built HTML parser/processor. This basically detects any type of visual newline and\n * allows it to be processed, useful for greentexting, mostly\n *\n * known issue: doesn't handle CDATA so nested CDATA might not work well\n *\n * @param {Object} input - input data\n * @param {(string) => string} processor - function that will be called on every line\n * @return {string} processed html\n */\nexport const processHtml = (html, processor) => {\n const handledTags = new Set(['p', 'br', 'div'])\n const openCloseTags = new Set(['p', 'div'])\n\n let buffer = '' // Current output buffer\n const level = [] // How deep we are in tags and which tags were there\n let textBuffer = '' // Current line content\n let tagBuffer = null // Current tag buffer, if null = we are not currently reading a tag\n\n // Extracts tag name from tag, i.e. => span\n const getTagName = (tag) => {\n const result = /(?:<\\/(\\w+)>|<(\\w+)\\s?[^/]*?\\/?>)/gi.exec(tag)\n return result && (result[1] || result[2])\n }\n\n const flush = () => { // Processes current line buffer, adds it to output buffer and clears line buffer\n if (textBuffer.trim().length > 0) {\n buffer += processor(textBuffer)\n } else {\n buffer += textBuffer\n }\n textBuffer = ''\n }\n\n const handleBr = (tag) => { // handles single newlines/linebreaks/selfclosing\n flush()\n buffer += tag\n }\n\n const handleOpen = (tag) => { // handles opening tags\n flush()\n buffer += tag\n level.push(tag)\n }\n\n const handleClose = (tag) => { // handles closing tags\n flush()\n buffer += tag\n if (level[level.length - 1] === tag) {\n level.pop()\n }\n }\n\n for (let i = 0; i < html.length; i++) {\n const char = html[i]\n if (char === '<' && tagBuffer === null) {\n tagBuffer = char\n } else if (char !== '>' && tagBuffer !== null) {\n tagBuffer += char\n } else if (char === '>' && tagBuffer !== null) {\n tagBuffer += char\n const tagFull = tagBuffer\n tagBuffer = null\n const tagName = getTagName(tagFull)\n if (handledTags.has(tagName)) {\n if (tagName === 'br') {\n handleBr(tagFull)\n } else if (openCloseTags.has(tagName)) {\n if (tagFull[1] === '/') {\n handleClose(tagFull)\n } else if (tagFull[tagFull.length - 2] === '/') {\n // self-closing\n handleBr(tagFull)\n } else {\n handleOpen(tagFull)\n }\n }\n } else {\n textBuffer += tagFull\n }\n } else if (char === '\\n') {\n handleBr(char)\n } else {\n textBuffer += char\n }\n }\n if (tagBuffer) {\n textBuffer += tagBuffer\n }\n\n flush()\n\n return buffer\n}\n","export const mentionMatchesUrl = (attention, url) => {\n if (url === attention.statusnet_profile_url) {\n return true\n }\n const [namepart, instancepart] = attention.screen_name.split('@')\n const matchstring = new RegExp('://' + instancepart + '/.*' + namepart + '$', 'g')\n\n return !!url.match(matchstring)\n}\n\n/**\n * Extract tag name from pleroma or mastodon url.\n * i.e https://bikeshed.party/tag/photo or https://quey.org/tags/sky\n * @param {string} url\n */\nexport const extractTagFromUrl = (url) => {\n const regex = /tag[s]*\\/(\\w+)$/g\n const result = regex.exec(url)\n if (!result) {\n return false\n }\n return result[1]\n}\n","function injectStyle (context) {\n require(\"!!vue-style-loader!css-loader?minimize!../../../node_modules/vue-loader/lib/style-compiler/index?{\\\"optionsId\\\":\\\"0\\\",\\\"vue\\\":true,\\\"scoped\\\":false,\\\"sourceMap\\\":false}!sass-loader!../../../node_modules/vue-loader/lib/selector?type=styles&index=0!./status.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./status.js\"\nimport __vue_script__ from \"!!babel-loader!./status.js\"/* template */\nimport {render as __vue_render__, staticRenderFns as __vue_static_render_fns__} from \"!!../../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-49a3be34\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./status.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nimport normalizeComponent from \"!../../../node_modules/vue-loader/lib/runtime/component-normalizer\"\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_render__,\n __vue_static_render_fns__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return (!_vm.hideStatus)?_c('div',{staticClass:\"status-el\",class:[{ 'status-el_focused': _vm.isFocused }, { 'status-conversation': _vm.inlineExpanded }]},[(_vm.error)?_c('div',{staticClass:\"alert error\"},[_vm._v(\"\\n \"+_vm._s(_vm.error)+\"\\n \"),_c('i',{staticClass:\"button-icon icon-cancel\",on:{\"click\":_vm.clearError}})]):_vm._e(),_vm._v(\" \"),(_vm.muted && !_vm.isPreview)?[_c('div',{staticClass:\"media status container muted\"},[_c('small',[_c('router-link',{attrs:{\"to\":_vm.userProfileLink}},[_vm._v(\"\\n \"+_vm._s(_vm.status.user.screen_name)+\"\\n \")])],1),_vm._v(\" \"),_c('small',{staticClass:\"muteWords\"},[_vm._v(_vm._s(_vm.muteWordHits.join(', ')))]),_vm._v(\" \"),_c('a',{staticClass:\"unmute\",attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();return _vm.toggleMute($event)}}},[_c('i',{staticClass:\"button-icon icon-eye-off\"})])])]:[(_vm.showPinned)?_c('div',{staticClass:\"status-pin\"},[_c('i',{staticClass:\"fa icon-pin faint\"}),_vm._v(\" \"),_c('span',{staticClass:\"faint\"},[_vm._v(_vm._s(_vm.$t('status.pinned')))])]):_vm._e(),_vm._v(\" \"),(_vm.retweet && !_vm.noHeading && !_vm.inConversation)?_c('div',{staticClass:\"media container retweet-info\",class:[_vm.repeaterClass, { highlighted: _vm.repeaterStyle }],style:([_vm.repeaterStyle])},[(_vm.retweet)?_c('UserAvatar',{staticClass:\"media-left\",attrs:{\"better-shadow\":_vm.betterShadow,\"user\":_vm.statusoid.user}}):_vm._e(),_vm._v(\" \"),_c('div',{staticClass:\"media-body faint\"},[_c('span',{staticClass:\"user-name\"},[(_vm.retweeterHtml)?_c('router-link',{attrs:{\"to\":_vm.retweeterProfileLink},domProps:{\"innerHTML\":_vm._s(_vm.retweeterHtml)}}):_c('router-link',{attrs:{\"to\":_vm.retweeterProfileLink}},[_vm._v(_vm._s(_vm.retweeter))])],1),_vm._v(\" \"),_c('i',{staticClass:\"fa icon-retweet retweeted\",attrs:{\"title\":_vm.$t('tool_tip.repeat')}}),_vm._v(\"\\n \"+_vm._s(_vm.$t('timeline.repeated'))+\"\\n \")])],1):_vm._e(),_vm._v(\" \"),_c('div',{staticClass:\"media status\",class:[_vm.userClass, { highlighted: _vm.userStyle, 'is-retweet': _vm.retweet && !_vm.inConversation }],style:([ _vm.userStyle ]),attrs:{\"data-tags\":_vm.tags}},[(!_vm.noHeading)?_c('div',{staticClass:\"media-left\"},[_c('router-link',{attrs:{\"to\":_vm.userProfileLink},nativeOn:{\"!click\":function($event){$event.stopPropagation();$event.preventDefault();return _vm.toggleUserExpanded($event)}}},[_c('UserAvatar',{attrs:{\"compact\":_vm.compact,\"better-shadow\":_vm.betterShadow,\"user\":_vm.status.user}})],1)],1):_vm._e(),_vm._v(\" \"),_c('div',{staticClass:\"status-body\"},[(_vm.userExpanded)?_c('UserCard',{staticClass:\"status-usercard\",attrs:{\"user\":_vm.status.user,\"rounded\":true,\"bordered\":true}}):_vm._e(),_vm._v(\" \"),(!_vm.noHeading)?_c('div',{staticClass:\"media-heading\"},[_c('div',{staticClass:\"heading-name-row\"},[_c('div',{staticClass:\"name-and-account-name\"},[(_vm.status.user.name_html)?_c('h4',{staticClass:\"user-name\",domProps:{\"innerHTML\":_vm._s(_vm.status.user.name_html)}}):_c('h4',{staticClass:\"user-name\"},[_vm._v(\"\\n \"+_vm._s(_vm.status.user.name)+\"\\n \")]),_vm._v(\" \"),_c('router-link',{staticClass:\"account-name\",attrs:{\"to\":_vm.userProfileLink}},[_vm._v(\"\\n \"+_vm._s(_vm.status.user.screen_name)+\"\\n \")])],1),_vm._v(\" \"),_c('span',{staticClass:\"heading-right\"},[_c('router-link',{staticClass:\"timeago faint-link\",attrs:{\"to\":{ name: 'conversation', params: { id: _vm.status.id } }}},[_c('Timeago',{attrs:{\"time\":_vm.status.created_at,\"auto-update\":60}})],1),_vm._v(\" \"),(_vm.status.visibility)?_c('div',{staticClass:\"button-icon visibility-icon\"},[_c('i',{class:_vm.visibilityIcon(_vm.status.visibility),attrs:{\"title\":_vm._f(\"capitalize\")(_vm.status.visibility)}})]):_vm._e(),_vm._v(\" \"),(!_vm.status.is_local && !_vm.isPreview)?_c('a',{staticClass:\"source_url\",attrs:{\"href\":_vm.status.external_url,\"target\":\"_blank\",\"title\":\"Source\"}},[_c('i',{staticClass:\"button-icon icon-link-ext-alt\"})]):_vm._e(),_vm._v(\" \"),(_vm.expandable && !_vm.isPreview)?[_c('a',{attrs:{\"href\":\"#\",\"title\":\"Expand\"},on:{\"click\":function($event){$event.preventDefault();return _vm.toggleExpanded($event)}}},[_c('i',{staticClass:\"button-icon icon-plus-squared\"})])]:_vm._e(),_vm._v(\" \"),(_vm.unmuted)?_c('a',{attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();return _vm.toggleMute($event)}}},[_c('i',{staticClass:\"button-icon icon-eye-off\"})]):_vm._e()],2)]),_vm._v(\" \"),_c('div',{staticClass:\"heading-reply-row\"},[(_vm.isReply)?_c('div',{staticClass:\"reply-to-and-accountname\"},[(!_vm.isPreview)?_c('StatusPopover',{staticClass:\"reply-to-popover\",staticStyle:{\"min-width\":\"0\"},attrs:{\"status-id\":_vm.status.in_reply_to_status_id}},[_c('a',{staticClass:\"reply-to\",attrs:{\"href\":\"#\",\"aria-label\":_vm.$t('tool_tip.reply')},on:{\"click\":function($event){$event.preventDefault();_vm.gotoOriginal(_vm.status.in_reply_to_status_id)}}},[_c('i',{staticClass:\"button-icon icon-reply\"}),_vm._v(\" \"),_c('span',{staticClass:\"faint-link reply-to-text\"},[_vm._v(_vm._s(_vm.$t('status.reply_to')))])])]):_c('span',{staticClass:\"reply-to\"},[_c('span',{staticClass:\"reply-to-text\"},[_vm._v(_vm._s(_vm.$t('status.reply_to')))])]),_vm._v(\" \"),_c('router-link',{attrs:{\"to\":_vm.replyProfileLink}},[_vm._v(\"\\n \"+_vm._s(_vm.replyToName)+\"\\n \")]),_vm._v(\" \"),(_vm.replies && _vm.replies.length)?_c('span',{staticClass:\"faint replies-separator\"},[_vm._v(\"\\n -\\n \")]):_vm._e()],1):_vm._e(),_vm._v(\" \"),(_vm.inConversation && !_vm.isPreview && _vm.replies && _vm.replies.length)?_c('div',{staticClass:\"replies\"},[_c('span',{staticClass:\"faint\"},[_vm._v(_vm._s(_vm.$t('status.replies_list')))]),_vm._v(\" \"),_vm._l((_vm.replies),function(reply){return _c('StatusPopover',{key:reply.id,attrs:{\"status-id\":reply.id}},[_c('a',{staticClass:\"reply-link\",attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();_vm.gotoOriginal(reply.id)}}},[_vm._v(_vm._s(reply.name))])])})],2):_vm._e()])]):_vm._e(),_vm._v(\" \"),(_vm.longSubject)?_c('div',{staticClass:\"status-content-wrapper\",class:{ 'tall-status': !_vm.showingLongSubject }},[(!_vm.showingLongSubject)?_c('a',{staticClass:\"tall-status-hider\",class:{ 'tall-status-hider_focused': _vm.isFocused },attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();_vm.showingLongSubject=true}}},[_vm._v(_vm._s(_vm.$t(\"general.show_more\")))]):_vm._e(),_vm._v(\" \"),_c('div',{staticClass:\"status-content media-body\",domProps:{\"innerHTML\":_vm._s(_vm.contentHtml)},on:{\"click\":function($event){$event.preventDefault();return _vm.linkClicked($event)}}}),_vm._v(\" \"),(_vm.showingLongSubject)?_c('a',{staticClass:\"status-unhider\",attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();_vm.showingLongSubject=false}}},[_vm._v(_vm._s(_vm.$t(\"general.show_less\")))]):_vm._e()]):_c('div',{staticClass:\"status-content-wrapper\",class:{'tall-status': _vm.hideTallStatus}},[(_vm.hideTallStatus)?_c('a',{staticClass:\"tall-status-hider\",class:{ 'tall-status-hider_focused': _vm.isFocused },attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();return _vm.toggleShowMore($event)}}},[_vm._v(_vm._s(_vm.$t(\"general.show_more\")))]):_vm._e(),_vm._v(\" \"),(!_vm.hideSubjectStatus)?_c('div',{staticClass:\"status-content media-body\",domProps:{\"innerHTML\":_vm._s(_vm.contentHtml)},on:{\"click\":function($event){$event.preventDefault();return _vm.linkClicked($event)}}}):_c('div',{staticClass:\"status-content media-body\",domProps:{\"innerHTML\":_vm._s(_vm.status.summary_html)},on:{\"click\":function($event){$event.preventDefault();return _vm.linkClicked($event)}}}),_vm._v(\" \"),(_vm.hideSubjectStatus)?_c('a',{staticClass:\"cw-status-hider\",attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();return _vm.toggleShowMore($event)}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"general.show_more\"))+\"\\n \"),(_vm.hasImageAttachments)?_c('span',{staticClass:\"icon-picture\"}):_vm._e(),_vm._v(\" \"),(_vm.hasVideoAttachments)?_c('span',{staticClass:\"icon-video\"}):_vm._e(),_vm._v(\" \"),(_vm.status.card)?_c('span',{staticClass:\"icon-link\"}):_vm._e()]):_vm._e(),_vm._v(\" \"),(_vm.showingMore)?_c('a',{staticClass:\"status-unhider\",attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();return _vm.toggleShowMore($event)}}},[_vm._v(_vm._s(_vm.$t(\"general.show_less\")))]):_vm._e()]),_vm._v(\" \"),(_vm.status.poll && _vm.status.poll.options)?_c('div',[_c('poll',{attrs:{\"base-poll\":_vm.status.poll}})],1):_vm._e(),_vm._v(\" \"),(_vm.status.attachments && (!_vm.hideSubjectStatus || _vm.showingLongSubject))?_c('div',{staticClass:\"attachments media-body\"},[_vm._l((_vm.nonGalleryAttachments),function(attachment){return _c('attachment',{key:attachment.id,staticClass:\"non-gallery\",attrs:{\"size\":_vm.attachmentSize,\"nsfw\":_vm.nsfwClickthrough,\"attachment\":attachment,\"allow-play\":true,\"set-media\":_vm.setMedia()}})}),_vm._v(\" \"),(_vm.galleryAttachments.length > 0)?_c('gallery',{attrs:{\"nsfw\":_vm.nsfwClickthrough,\"attachments\":_vm.galleryAttachments,\"set-media\":_vm.setMedia()}}):_vm._e()],2):_vm._e(),_vm._v(\" \"),(_vm.status.card && !_vm.hideSubjectStatus && !_vm.noHeading)?_c('div',{staticClass:\"link-preview media-body\"},[_c('link-preview',{attrs:{\"card\":_vm.status.card,\"size\":_vm.attachmentSize,\"nsfw\":_vm.nsfwClickthrough}})],1):_vm._e(),_vm._v(\" \"),_c('transition',{attrs:{\"name\":\"fade\"}},[(!_vm.hidePostStats && _vm.isFocused && _vm.combinedFavsAndRepeatsUsers.length > 0)?_c('div',{staticClass:\"favs-repeated-users\"},[_c('div',{staticClass:\"stats\"},[(_vm.statusFromGlobalRepository.rebloggedBy && _vm.statusFromGlobalRepository.rebloggedBy.length > 0)?_c('div',{staticClass:\"stat-count\"},[_c('a',{staticClass:\"stat-title\"},[_vm._v(_vm._s(_vm.$t('status.repeats')))]),_vm._v(\" \"),_c('div',{staticClass:\"stat-number\"},[_vm._v(\"\\n \"+_vm._s(_vm.statusFromGlobalRepository.rebloggedBy.length)+\"\\n \")])]):_vm._e(),_vm._v(\" \"),(_vm.statusFromGlobalRepository.favoritedBy && _vm.statusFromGlobalRepository.favoritedBy.length > 0)?_c('div',{staticClass:\"stat-count\"},[_c('a',{staticClass:\"stat-title\"},[_vm._v(_vm._s(_vm.$t('status.favorites')))]),_vm._v(\" \"),_c('div',{staticClass:\"stat-number\"},[_vm._v(\"\\n \"+_vm._s(_vm.statusFromGlobalRepository.favoritedBy.length)+\"\\n \")])]):_vm._e(),_vm._v(\" \"),_c('div',{staticClass:\"avatar-row\"},[_c('AvatarList',{attrs:{\"users\":_vm.combinedFavsAndRepeatsUsers}})],1)])]):_vm._e()]),_vm._v(\" \"),((_vm.mergedConfig.emojiReactionsOnTimeline || _vm.isFocused) && (!_vm.noHeading && !_vm.isPreview))?_c('EmojiReactions',{attrs:{\"status\":_vm.status}}):_vm._e(),_vm._v(\" \"),(!_vm.noHeading && !_vm.isPreview)?_c('div',{staticClass:\"status-actions media-body\"},[_c('div',[(_vm.loggedIn)?_c('i',{staticClass:\"button-icon icon-reply\",class:{'button-icon-active': _vm.replying},attrs:{\"title\":_vm.$t('tool_tip.reply')},on:{\"click\":function($event){$event.preventDefault();return _vm.toggleReplying($event)}}}):_c('i',{staticClass:\"button-icon button-icon-disabled icon-reply\",attrs:{\"title\":_vm.$t('tool_tip.reply')}}),_vm._v(\" \"),(_vm.status.replies_count > 0)?_c('span',[_vm._v(_vm._s(_vm.status.replies_count))]):_vm._e()]),_vm._v(\" \"),_c('retweet-button',{attrs:{\"visibility\":_vm.status.visibility,\"logged-in\":_vm.loggedIn,\"status\":_vm.status}}),_vm._v(\" \"),_c('favorite-button',{attrs:{\"logged-in\":_vm.loggedIn,\"status\":_vm.status}}),_vm._v(\" \"),_c('ReactButton',{attrs:{\"logged-in\":_vm.loggedIn,\"status\":_vm.status}}),_vm._v(\" \"),_c('extra-buttons',{attrs:{\"status\":_vm.status},on:{\"onError\":_vm.showError,\"onSuccess\":_vm.clearError}})],1):_vm._e()],1)]),_vm._v(\" \"),(_vm.replying)?_c('div',{staticClass:\"container\"},[_c('PostStatusForm',{staticClass:\"reply-body\",attrs:{\"reply-to\":_vm.status.id,\"attentions\":_vm.status.attentions,\"replied-user\":_vm.status.user,\"copy-message-scope\":_vm.status.visibility,\"subject\":_vm.replySubject},on:{\"posted\":_vm.toggleReplying}})],1):_vm._e()]],2):_vm._e()}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","\nconst Popover = {\n name: 'Popover',\n props: {\n // Action to trigger popover: either 'hover' or 'click'\n trigger: String,\n // Either 'top' or 'bottom'\n placement: String,\n // Takes object with properties 'x' and 'y', values of these can be\n // 'container' for using offsetParent as boundaries for either axis\n // or 'viewport'\n boundTo: Object,\n // Takes a top/bottom/left/right object, how much space to leave\n // between boundary and popover element\n margin: Object,\n // Takes a x/y object and tells how many pixels to offset from\n // anchor point on either axis\n offset: Object,\n // Additional styles you may want for the popover container\n popoverClass: String\n },\n data () {\n return {\n hidden: true,\n styles: { opacity: 0 },\n oldSize: { width: 0, height: 0 }\n }\n },\n methods: {\n updateStyles () {\n if (this.hidden) {\n this.styles = {\n opacity: 0\n }\n return\n }\n\n // Popover will be anchored around this element, trigger ref is the container, so\n // its children are what are inside the slot. Expect only one slot=\"trigger\".\n const anchorEl = (this.$refs.trigger && this.$refs.trigger.children[0]) || this.$el\n const screenBox = anchorEl.getBoundingClientRect()\n // Screen position of the origin point for popover\n const origin = { x: screenBox.left + screenBox.width * 0.5, y: screenBox.top }\n const content = this.$refs.content\n // Minor optimization, don't call a slow reflow call if we don't have to\n const parentBounds = this.boundTo &&\n (this.boundTo.x === 'container' || this.boundTo.y === 'container') &&\n this.$el.offsetParent.getBoundingClientRect()\n const margin = this.margin || {}\n\n // What are the screen bounds for the popover? Viewport vs container\n // when using viewport, using default margin values to dodge the navbar\n const xBounds = this.boundTo && this.boundTo.x === 'container' ? {\n min: parentBounds.left + (margin.left || 0),\n max: parentBounds.right - (margin.right || 0)\n } : {\n min: 0 + (margin.left || 10),\n max: window.innerWidth - (margin.right || 10)\n }\n\n const yBounds = this.boundTo && this.boundTo.y === 'container' ? {\n min: parentBounds.top + (margin.top || 0),\n max: parentBounds.bottom - (margin.bottom || 0)\n } : {\n min: 0 + (margin.top || 50),\n max: window.innerHeight - (margin.bottom || 5)\n }\n\n let horizOffset = 0\n\n // If overflowing from left, move it so that it doesn't\n if ((origin.x - content.offsetWidth * 0.5) < xBounds.min) {\n horizOffset += -(origin.x - content.offsetWidth * 0.5) + xBounds.min\n }\n\n // If overflowing from right, move it so that it doesn't\n if ((origin.x + horizOffset + content.offsetWidth * 0.5) > xBounds.max) {\n horizOffset -= (origin.x + horizOffset + content.offsetWidth * 0.5) - xBounds.max\n }\n\n // Default to whatever user wished with placement prop\n let usingTop = this.placement !== 'bottom'\n\n // Handle special cases, first force to displaying on top if there's not space on bottom,\n // regardless of what placement value was. Then check if there's not space on top, and\n // force to bottom, again regardless of what placement value was.\n if (origin.y + content.offsetHeight > yBounds.max) usingTop = true\n if (origin.y - content.offsetHeight < yBounds.min) usingTop = false\n\n const yOffset = (this.offset && this.offset.y) || 0\n const translateY = usingTop\n ? -anchorEl.offsetHeight - yOffset - content.offsetHeight\n : yOffset\n\n const xOffset = (this.offset && this.offset.x) || 0\n const translateX = (anchorEl.offsetWidth * 0.5) - content.offsetWidth * 0.5 + horizOffset + xOffset\n\n // Note, separate translateX and translateY avoids blurry text on chromium,\n // single translate or translate3d resulted in blurry text.\n this.styles = {\n opacity: 1,\n transform: `translateX(${Math.floor(translateX)}px) translateY(${Math.floor(translateY)}px)`\n }\n },\n showPopover () {\n if (this.hidden) this.$emit('show')\n this.hidden = false\n this.$nextTick(this.updateStyles)\n },\n hidePopover () {\n if (!this.hidden) this.$emit('close')\n this.hidden = true\n this.styles = { opacity: 0 }\n },\n onMouseenter (e) {\n if (this.trigger === 'hover') this.showPopover()\n },\n onMouseleave (e) {\n if (this.trigger === 'hover') this.hidePopover()\n },\n onClick (e) {\n if (this.trigger === 'click') {\n if (this.hidden) {\n this.showPopover()\n } else {\n this.hidePopover()\n }\n }\n },\n onClickOutside (e) {\n if (this.hidden) return\n if (this.$el.contains(e.target)) return\n this.hidePopover()\n }\n },\n updated () {\n // Monitor changes to content size, update styles only when content sizes have changed,\n // that should be the only time we need to move the popover box if we don't care about scroll\n // or resize\n const content = this.$refs.content\n if (!content) return\n if (this.oldSize.width !== content.offsetWidth || this.oldSize.height !== content.offsetHeight) {\n this.updateStyles()\n this.oldSize = { width: content.offsetWidth, height: content.offsetHeight }\n }\n },\n created () {\n document.addEventListener('click', this.onClickOutside)\n },\n destroyed () {\n document.removeEventListener('click', this.onClickOutside)\n this.hidePopover()\n }\n}\n\nexport default Popover\n","function injectStyle (context) {\n require(\"!!vue-style-loader!css-loader?minimize!../../../node_modules/vue-loader/lib/style-compiler/index?{\\\"optionsId\\\":\\\"0\\\",\\\"vue\\\":true,\\\"scoped\\\":false,\\\"sourceMap\\\":false}!sass-loader!../../../node_modules/vue-loader/lib/selector?type=styles&index=0!./popover.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./popover.js\"\nimport __vue_script__ from \"!!babel-loader!./popover.js\"/* template */\nimport {render as __vue_render__, staticRenderFns as __vue_static_render_fns__} from \"!!../../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-10f1984d\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./popover.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nimport normalizeComponent from \"!../../../node_modules/vue-loader/lib/runtime/component-normalizer\"\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_render__,\n __vue_static_render_fns__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{on:{\"mouseenter\":_vm.onMouseenter,\"mouseleave\":_vm.onMouseleave}},[_c('div',{ref:\"trigger\",on:{\"click\":_vm.onClick}},[_vm._t(\"trigger\")],2),_vm._v(\" \"),(!_vm.hidden)?_c('div',{ref:\"content\",staticClass:\"popover\",class:_vm.popoverClass,style:(_vm.styles)},[_vm._t(\"content\",null,{close:_vm.hidePopover})],2):_vm._e()])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","export const SECOND = 1000\nexport const MINUTE = 60 * SECOND\nexport const HOUR = 60 * MINUTE\nexport const DAY = 24 * HOUR\nexport const WEEK = 7 * DAY\nexport const MONTH = 30 * DAY\nexport const YEAR = 365.25 * DAY\n\nexport const relativeTime = (date, nowThreshold = 1) => {\n if (typeof date === 'string') date = Date.parse(date)\n const round = Date.now() > date ? Math.floor : Math.ceil\n const d = Math.abs(Date.now() - date)\n let r = { num: round(d / YEAR), key: 'time.years' }\n if (d < nowThreshold * SECOND) {\n r.num = 0\n r.key = 'time.now'\n } else if (d < MINUTE) {\n r.num = round(d / SECOND)\n r.key = 'time.seconds'\n } else if (d < HOUR) {\n r.num = round(d / MINUTE)\n r.key = 'time.minutes'\n } else if (d < DAY) {\n r.num = round(d / HOUR)\n r.key = 'time.hours'\n } else if (d < WEEK) {\n r.num = round(d / DAY)\n r.key = 'time.days'\n } else if (d < MONTH) {\n r.num = round(d / WEEK)\n r.key = 'time.weeks'\n } else if (d < YEAR) {\n r.num = round(d / MONTH)\n r.key = 'time.months'\n }\n // Remove plural form when singular\n if (r.num === 1) r.key = r.key.slice(0, -1)\n return r\n}\n\nexport const relativeTimeShort = (date, nowThreshold = 1) => {\n const r = relativeTime(date, nowThreshold)\n r.key += '_short'\n return r\n}\n","\n\n\n","/* script */\nexport * from \"!!babel-loader!../../../node_modules/vue-loader/lib/selector?type=script&index=0!./progress_button.vue\"\nimport __vue_script__ from \"!!babel-loader!../../../node_modules/vue-loader/lib/selector?type=script&index=0!./progress_button.vue\"\n/* template */\nimport {render as __vue_render__, staticRenderFns as __vue_static_render_fns__} from \"!!../../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-9f751ae6\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./progress_button.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = null\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nimport normalizeComponent from \"!../../../node_modules/vue-loader/lib/runtime/component-normalizer\"\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_render__,\n __vue_static_render_fns__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('button',{attrs:{\"disabled\":_vm.progress || _vm.disabled},on:{\"click\":_vm.onClick}},[(_vm.progress && _vm.$slots.progress)?[_vm._t(\"progress\")]:[_vm._t(\"default\")]],2)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","import { hex2rgb } from '../color_convert/color_convert.js'\nconst highlightStyle = (prefs) => {\n if (prefs === undefined) return\n const { color, type } = prefs\n if (typeof color !== 'string') return\n const rgb = hex2rgb(color)\n if (rgb == null) return\n const solidColor = `rgb(${Math.floor(rgb.r)}, ${Math.floor(rgb.g)}, ${Math.floor(rgb.b)})`\n const tintColor = `rgba(${Math.floor(rgb.r)}, ${Math.floor(rgb.g)}, ${Math.floor(rgb.b)}, .1)`\n const tintColor2 = `rgba(${Math.floor(rgb.r)}, ${Math.floor(rgb.g)}, ${Math.floor(rgb.b)}, .2)`\n if (type === 'striped') {\n return {\n backgroundImage: [\n 'repeating-linear-gradient(135deg,',\n `${tintColor} ,`,\n `${tintColor} 20px,`,\n `${tintColor2} 20px,`,\n `${tintColor2} 40px`\n ].join(' '),\n backgroundPosition: '0 0'\n }\n } else if (type === 'solid') {\n return {\n backgroundColor: tintColor2\n }\n } else if (type === 'side') {\n return {\n backgroundImage: [\n 'linear-gradient(to right,',\n `${solidColor} ,`,\n `${solidColor} 2px,`,\n `transparent 6px`\n ].join(' '),\n backgroundPosition: '0 0'\n }\n }\n}\n\nconst highlightClass = (user) => {\n return 'USER____' + user.screen_name\n .replace(/\\./g, '_')\n .replace(/@/g, '_AT_')\n}\n\nexport {\n highlightClass,\n highlightStyle\n}\n","import Vue from 'vue'\n\nimport './tab_switcher.scss'\n\nexport default Vue.component('tab-switcher', {\n name: 'TabSwitcher',\n props: {\n renderOnlyFocused: {\n required: false,\n type: Boolean,\n default: false\n },\n onSwitch: {\n required: false,\n type: Function,\n default: undefined\n },\n activeTab: {\n required: false,\n type: String,\n default: undefined\n },\n scrollableTabs: {\n required: false,\n type: Boolean,\n default: false\n }\n },\n data () {\n return {\n active: this.$slots.default.findIndex(_ => _.tag)\n }\n },\n computed: {\n activeIndex () {\n // In case of controlled component\n if (this.activeTab) {\n return this.$slots.default.findIndex(slot => this.activeTab === slot.key)\n } else {\n return this.active\n }\n }\n },\n beforeUpdate () {\n const currentSlot = this.$slots.default[this.active]\n if (!currentSlot.tag) {\n this.active = this.$slots.default.findIndex(_ => _.tag)\n }\n },\n methods: {\n activateTab (index) {\n return (e) => {\n e.preventDefault()\n if (typeof this.onSwitch === 'function') {\n this.onSwitch.call(null, this.$slots.default[index].key)\n }\n this.active = index\n }\n }\n },\n render (h) {\n const tabs = this.$slots.default\n .map((slot, index) => {\n if (!slot.tag) return\n const classesTab = ['tab']\n const classesWrapper = ['tab-wrapper']\n\n if (this.activeIndex === index) {\n classesTab.push('active')\n classesWrapper.push('active')\n }\n if (slot.data.attrs.image) {\n return (\n

\n \n \n {slot.data.attrs.label ? '' : slot.data.attrs.label}\n \n
\n )\n }\n return (\n
\n \n {slot.data.attrs.label}\n
\n )\n })\n\n const contents = this.$slots.default.map((slot, index) => {\n if (!slot.tag) return\n const active = this.activeIndex === index\n if (this.renderOnlyFocused) {\n return active\n ?
{slot}
\n :
\n }\n return
{slot}
\n })\n\n return (\n
\n
\n {tabs}\n
\n
\n {contents}\n
\n
\n )\n }\n})\n","/* eslint-env browser */\nimport statusPosterService from '../../services/status_poster/status_poster.service.js'\nimport fileSizeFormatService from '../../services/file_size_format/file_size_format.js'\n\nconst mediaUpload = {\n data () {\n return {\n uploading: false,\n uploadReady: true\n }\n },\n methods: {\n uploadFile (file) {\n const self = this\n const store = this.$store\n if (file.size > store.state.instance.uploadlimit) {\n const filesize = fileSizeFormatService.fileSizeFormat(file.size)\n const allowedsize = fileSizeFormatService.fileSizeFormat(store.state.instance.uploadlimit)\n self.$emit('upload-failed', 'file_too_big', { filesize: filesize.num, filesizeunit: filesize.unit, allowedsize: allowedsize.num, allowedsizeunit: allowedsize.unit })\n return\n }\n const formData = new FormData()\n formData.append('file', file)\n\n self.$emit('uploading')\n self.uploading = true\n\n statusPosterService.uploadMedia({ store, formData })\n .then((fileData) => {\n self.$emit('uploaded', fileData)\n self.uploading = false\n }, (error) => { // eslint-disable-line handle-callback-err\n self.$emit('upload-failed', 'default')\n self.uploading = false\n })\n },\n fileDrop (e) {\n if (e.dataTransfer.files.length > 0) {\n e.preventDefault() // allow dropping text like before\n this.uploadFile(e.dataTransfer.files[0])\n }\n },\n fileDrag (e) {\n let types = e.dataTransfer.types\n if (types.contains('Files')) {\n e.dataTransfer.dropEffect = 'copy'\n } else {\n e.dataTransfer.dropEffect = 'none'\n }\n },\n clearFile () {\n this.uploadReady = false\n this.$nextTick(() => {\n this.uploadReady = true\n })\n },\n change ({ target }) {\n for (var i = 0; i < target.files.length; i++) {\n let file = target.files[i]\n this.uploadFile(file)\n }\n }\n },\n props: [\n 'dropFiles'\n ],\n watch: {\n 'dropFiles': function (fileInfos) {\n if (!this.uploading) {\n this.uploadFile(fileInfos[0])\n }\n }\n }\n}\n\nexport default mediaUpload\n","function injectStyle (context) {\n require(\"!!vue-style-loader!css-loader?minimize!../../../node_modules/vue-loader/lib/style-compiler/index?{\\\"optionsId\\\":\\\"0\\\",\\\"vue\\\":true,\\\"scoped\\\":false,\\\"sourceMap\\\":false}!sass-loader!../../../node_modules/vue-loader/lib/selector?type=styles&index=0!./media_upload.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./media_upload.js\"\nimport __vue_script__ from \"!!babel-loader!./media_upload.js\"/* template */\nimport {render as __vue_render__, staticRenderFns as __vue_static_render_fns__} from \"!!../../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-74382032\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./media_upload.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nimport normalizeComponent from \"!../../../node_modules/vue-loader/lib/runtime/component-normalizer\"\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_render__,\n __vue_static_render_fns__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"media-upload\",on:{\"drop\":[function($event){$event.preventDefault();},_vm.fileDrop],\"dragover\":function($event){$event.preventDefault();return _vm.fileDrag($event)}}},[_c('label',{staticClass:\"label\",attrs:{\"title\":_vm.$t('tool_tip.media_upload')}},[(_vm.uploading)?_c('i',{staticClass:\"progress-icon icon-spin4 animate-spin\"}):_vm._e(),_vm._v(\" \"),(!_vm.uploading)?_c('i',{staticClass:\"new-icon icon-upload\"}):_vm._e(),_vm._v(\" \"),(_vm.uploadReady)?_c('input',{staticStyle:{\"position\":\"fixed\",\"top\":\"-100em\"},attrs:{\"type\":\"file\",\"multiple\":\"true\"},on:{\"change\":_vm.change}}):_vm._e()])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","import * as DateUtils from 'src/services/date_utils/date_utils.js'\nimport { uniq } from 'lodash'\n\nexport default {\n name: 'PollForm',\n props: ['visible'],\n data: () => ({\n pollType: 'single',\n options: ['', ''],\n expiryAmount: 10,\n expiryUnit: 'minutes'\n }),\n computed: {\n pollLimits () {\n return this.$store.state.instance.pollLimits\n },\n maxOptions () {\n return this.pollLimits.max_options\n },\n maxLength () {\n return this.pollLimits.max_option_chars\n },\n expiryUnits () {\n const allUnits = ['minutes', 'hours', 'days']\n const expiry = this.convertExpiryFromUnit\n return allUnits.filter(\n unit => this.pollLimits.max_expiration >= expiry(unit, 1)\n )\n },\n minExpirationInCurrentUnit () {\n return Math.ceil(\n this.convertExpiryToUnit(\n this.expiryUnit,\n this.pollLimits.min_expiration\n )\n )\n },\n maxExpirationInCurrentUnit () {\n return Math.floor(\n this.convertExpiryToUnit(\n this.expiryUnit,\n this.pollLimits.max_expiration\n )\n )\n }\n },\n methods: {\n clear () {\n this.pollType = 'single'\n this.options = ['', '']\n this.expiryAmount = 10\n this.expiryUnit = 'minutes'\n },\n nextOption (index) {\n const element = this.$el.querySelector(`#poll-${index + 1}`)\n if (element) {\n element.focus()\n } else {\n // Try adding an option and try focusing on it\n const addedOption = this.addOption()\n if (addedOption) {\n this.$nextTick(function () {\n this.nextOption(index)\n })\n }\n }\n },\n addOption () {\n if (this.options.length < this.maxOptions) {\n this.options.push('')\n return true\n }\n return false\n },\n deleteOption (index, event) {\n if (this.options.length > 2) {\n this.options.splice(index, 1)\n }\n },\n convertExpiryToUnit (unit, amount) {\n // Note: we want seconds and not milliseconds\n switch (unit) {\n case 'minutes': return (1000 * amount) / DateUtils.MINUTE\n case 'hours': return (1000 * amount) / DateUtils.HOUR\n case 'days': return (1000 * amount) / DateUtils.DAY\n }\n },\n convertExpiryFromUnit (unit, amount) {\n // Note: we want seconds and not milliseconds\n switch (unit) {\n case 'minutes': return 0.001 * amount * DateUtils.MINUTE\n case 'hours': return 0.001 * amount * DateUtils.HOUR\n case 'days': return 0.001 * amount * DateUtils.DAY\n }\n },\n expiryAmountChange () {\n this.expiryAmount =\n Math.max(this.minExpirationInCurrentUnit, this.expiryAmount)\n this.expiryAmount =\n Math.min(this.maxExpirationInCurrentUnit, this.expiryAmount)\n this.updatePollToParent()\n },\n updatePollToParent () {\n const expiresIn = this.convertExpiryFromUnit(\n this.expiryUnit,\n this.expiryAmount\n )\n\n const options = uniq(this.options.filter(option => option !== ''))\n if (options.length < 2) {\n this.$emit('update-poll', { error: this.$t('polls.not_enough_options') })\n return\n }\n this.$emit('update-poll', {\n options,\n multiple: this.pollType === 'multiple',\n expiresIn\n })\n }\n }\n}\n","function injectStyle (context) {\n require(\"!!vue-style-loader!css-loader?minimize!../../../node_modules/vue-loader/lib/style-compiler/index?{\\\"optionsId\\\":\\\"0\\\",\\\"vue\\\":true,\\\"scoped\\\":false,\\\"sourceMap\\\":false}!sass-loader!../../../node_modules/vue-loader/lib/selector?type=styles&index=0!./poll_form.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./poll_form.js\"\nimport __vue_script__ from \"!!babel-loader!./poll_form.js\"/* template */\nimport {render as __vue_render__, staticRenderFns as __vue_static_render_fns__} from \"!!../../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-1f896331\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./poll_form.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nimport normalizeComponent from \"!../../../node_modules/vue-loader/lib/runtime/component-normalizer\"\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_render__,\n __vue_static_render_fns__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return (_vm.visible)?_c('div',{staticClass:\"poll-form\"},[_vm._l((_vm.options),function(option,index){return _c('div',{key:index,staticClass:\"poll-option\"},[_c('div',{staticClass:\"input-container\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.options[index]),expression:\"options[index]\"}],staticClass:\"poll-option-input\",attrs:{\"id\":(\"poll-\" + index),\"type\":\"text\",\"placeholder\":_vm.$t('polls.option'),\"maxlength\":_vm.maxLength},domProps:{\"value\":(_vm.options[index])},on:{\"change\":_vm.updatePollToParent,\"keydown\":function($event){if(!('button' in $event)&&_vm._k($event.keyCode,\"enter\",13,$event.key,\"Enter\")){ return null; }$event.stopPropagation();$event.preventDefault();_vm.nextOption(index)},\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.options, index, $event.target.value)}}})]),_vm._v(\" \"),(_vm.options.length > 2)?_c('div',{staticClass:\"icon-container\"},[_c('i',{staticClass:\"icon-cancel\",on:{\"click\":function($event){_vm.deleteOption(index)}}})]):_vm._e()])}),_vm._v(\" \"),(_vm.options.length < _vm.maxOptions)?_c('a',{staticClass:\"add-option faint\",on:{\"click\":_vm.addOption}},[_c('i',{staticClass:\"icon-plus\"}),_vm._v(\"\\n \"+_vm._s(_vm.$t(\"polls.add_option\"))+\"\\n \")]):_vm._e(),_vm._v(\" \"),_c('div',{staticClass:\"poll-type-expiry\"},[_c('div',{staticClass:\"poll-type\",attrs:{\"title\":_vm.$t('polls.type')}},[_c('label',{staticClass:\"select\",attrs:{\"for\":\"poll-type-selector\"}},[_c('select',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.pollType),expression:\"pollType\"}],staticClass:\"select\",on:{\"change\":[function($event){var $$selectedVal = Array.prototype.filter.call($event.target.options,function(o){return o.selected}).map(function(o){var val = \"_value\" in o ? o._value : o.value;return val}); _vm.pollType=$event.target.multiple ? $$selectedVal : $$selectedVal[0]},_vm.updatePollToParent]}},[_c('option',{attrs:{\"value\":\"single\"}},[_vm._v(_vm._s(_vm.$t('polls.single_choice')))]),_vm._v(\" \"),_c('option',{attrs:{\"value\":\"multiple\"}},[_vm._v(_vm._s(_vm.$t('polls.multiple_choices')))])]),_vm._v(\" \"),_c('i',{staticClass:\"icon-down-open\"})])]),_vm._v(\" \"),_c('div',{staticClass:\"poll-expiry\",attrs:{\"title\":_vm.$t('polls.expiry')}},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.expiryAmount),expression:\"expiryAmount\"}],staticClass:\"expiry-amount hide-number-spinner\",attrs:{\"type\":\"number\",\"min\":_vm.minExpirationInCurrentUnit,\"max\":_vm.maxExpirationInCurrentUnit},domProps:{\"value\":(_vm.expiryAmount)},on:{\"change\":_vm.expiryAmountChange,\"input\":function($event){if($event.target.composing){ return; }_vm.expiryAmount=$event.target.value}}}),_vm._v(\" \"),_c('label',{staticClass:\"expiry-unit select\"},[_c('select',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.expiryUnit),expression:\"expiryUnit\"}],on:{\"change\":[function($event){var $$selectedVal = Array.prototype.filter.call($event.target.options,function(o){return o.selected}).map(function(o){var val = \"_value\" in o ? o._value : o.value;return val}); _vm.expiryUnit=$event.target.multiple ? $$selectedVal : $$selectedVal[0]},_vm.expiryAmountChange]}},_vm._l((_vm.expiryUnits),function(unit){return _c('option',{key:unit,domProps:{\"value\":unit}},[_vm._v(\"\\n \"+_vm._s(_vm.$t((\"time.\" + unit + \"_short\"), ['']))+\"\\n \")])}),0),_vm._v(\" \"),_c('i',{staticClass:\"icon-down-open\"})])])])],2):_vm._e()}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","import statusPoster from '../../services/status_poster/status_poster.service.js'\nimport MediaUpload from '../media_upload/media_upload.vue'\nimport ScopeSelector from '../scope_selector/scope_selector.vue'\nimport EmojiInput from '../emoji_input/emoji_input.vue'\nimport PollForm from '../poll/poll_form.vue'\nimport fileTypeService from '../../services/file_type/file_type.service.js'\nimport { findOffset } from '../../services/offset_finder/offset_finder.service.js'\nimport { reject, map, uniqBy } from 'lodash'\nimport suggestor from '../emoji_input/suggestor.js'\nimport { mapGetters } from 'vuex'\nimport Checkbox from '../checkbox/checkbox.vue'\n\nconst buildMentionsString = ({ user, attentions = [] }, currentUser) => {\n let allAttentions = [...attentions]\n\n allAttentions.unshift(user)\n\n allAttentions = uniqBy(allAttentions, 'id')\n allAttentions = reject(allAttentions, { id: currentUser.id })\n\n let mentions = map(allAttentions, (attention) => {\n return `@${attention.screen_name}`\n })\n\n return mentions.length > 0 ? mentions.join(' ') + ' ' : ''\n}\n\nconst PostStatusForm = {\n props: [\n 'replyTo',\n 'repliedUser',\n 'attentions',\n 'copyMessageScope',\n 'subject'\n ],\n components: {\n MediaUpload,\n EmojiInput,\n PollForm,\n ScopeSelector,\n Checkbox\n },\n mounted () {\n this.resize(this.$refs.textarea)\n const textLength = this.$refs.textarea.value.length\n this.$refs.textarea.setSelectionRange(textLength, textLength)\n\n if (this.replyTo) {\n this.$refs.textarea.focus()\n }\n },\n data () {\n const preset = this.$route.query.message\n let statusText = preset || ''\n\n const { scopeCopy } = this.$store.getters.mergedConfig\n\n if (this.replyTo) {\n const currentUser = this.$store.state.users.currentUser\n statusText = buildMentionsString({ user: this.repliedUser, attentions: this.attentions }, currentUser)\n }\n\n const scope = ((this.copyMessageScope && scopeCopy) || this.copyMessageScope === 'direct')\n ? this.copyMessageScope\n : this.$store.state.users.currentUser.default_scope\n\n const { postContentType: contentType } = this.$store.getters.mergedConfig\n\n return {\n dropFiles: [],\n submitDisabled: false,\n error: null,\n posting: false,\n highlighted: 0,\n newStatus: {\n spoilerText: this.subject || '',\n status: statusText,\n nsfw: false,\n files: [],\n poll: {},\n visibility: scope,\n contentType\n },\n caret: 0,\n pollFormVisible: false\n }\n },\n computed: {\n users () {\n return this.$store.state.users.users\n },\n userDefaultScope () {\n return this.$store.state.users.currentUser.default_scope\n },\n showAllScopes () {\n return !this.mergedConfig.minimalScopesMode\n },\n emojiUserSuggestor () {\n return suggestor({\n emoji: [\n ...this.$store.state.instance.emoji,\n ...this.$store.state.instance.customEmoji\n ],\n users: this.$store.state.users.users,\n updateUsersList: (input) => this.$store.dispatch('searchUsers', input)\n })\n },\n emojiSuggestor () {\n return suggestor({\n emoji: [\n ...this.$store.state.instance.emoji,\n ...this.$store.state.instance.customEmoji\n ]\n })\n },\n emoji () {\n return this.$store.state.instance.emoji || []\n },\n customEmoji () {\n return this.$store.state.instance.customEmoji || []\n },\n statusLength () {\n return this.newStatus.status.length\n },\n spoilerTextLength () {\n return this.newStatus.spoilerText.length\n },\n statusLengthLimit () {\n return this.$store.state.instance.textlimit\n },\n hasStatusLengthLimit () {\n return this.statusLengthLimit > 0\n },\n charactersLeft () {\n return this.statusLengthLimit - (this.statusLength + this.spoilerTextLength)\n },\n isOverLengthLimit () {\n return this.hasStatusLengthLimit && (this.charactersLeft < 0)\n },\n minimalScopesMode () {\n return this.$store.state.instance.minimalScopesMode\n },\n alwaysShowSubject () {\n return this.mergedConfig.alwaysShowSubjectInput\n },\n postFormats () {\n return this.$store.state.instance.postFormats || []\n },\n safeDMEnabled () {\n return this.$store.state.instance.safeDM\n },\n pollsAvailable () {\n return this.$store.state.instance.pollsAvailable &&\n this.$store.state.instance.pollLimits.max_options >= 2\n },\n hideScopeNotice () {\n return this.$store.getters.mergedConfig.hideScopeNotice\n },\n pollContentError () {\n return this.pollFormVisible &&\n this.newStatus.poll &&\n this.newStatus.poll.error\n },\n ...mapGetters(['mergedConfig'])\n },\n methods: {\n postStatus (newStatus) {\n if (this.posting) { return }\n if (this.submitDisabled) { return }\n\n if (this.newStatus.status === '') {\n if (this.newStatus.files.length === 0) {\n this.error = 'Cannot post an empty status with no files'\n return\n }\n }\n\n const poll = this.pollFormVisible ? this.newStatus.poll : {}\n if (this.pollContentError) {\n this.error = this.pollContentError\n return\n }\n\n this.posting = true\n statusPoster.postStatus({\n status: newStatus.status,\n spoilerText: newStatus.spoilerText || null,\n visibility: newStatus.visibility,\n sensitive: newStatus.nsfw,\n media: newStatus.files,\n store: this.$store,\n inReplyToStatusId: this.replyTo,\n contentType: newStatus.contentType,\n poll\n }).then((data) => {\n if (!data.error) {\n this.newStatus = {\n status: '',\n spoilerText: '',\n files: [],\n visibility: newStatus.visibility,\n contentType: newStatus.contentType,\n poll: {}\n }\n this.pollFormVisible = false\n this.$refs.mediaUpload.clearFile()\n this.clearPollForm()\n this.$emit('posted')\n let el = this.$el.querySelector('textarea')\n el.style.height = 'auto'\n el.style.height = undefined\n this.error = null\n } else {\n this.error = data.error\n }\n this.posting = false\n })\n },\n addMediaFile (fileInfo) {\n this.newStatus.files.push(fileInfo)\n this.enableSubmit()\n },\n removeMediaFile (fileInfo) {\n let index = this.newStatus.files.indexOf(fileInfo)\n this.newStatus.files.splice(index, 1)\n },\n uploadFailed (errString, templateArgs) {\n templateArgs = templateArgs || {}\n this.error = this.$t('upload.error.base') + ' ' + this.$t('upload.error.' + errString, templateArgs)\n this.enableSubmit()\n },\n disableSubmit () {\n this.submitDisabled = true\n },\n enableSubmit () {\n this.submitDisabled = false\n },\n type (fileInfo) {\n return fileTypeService.fileType(fileInfo.mimetype)\n },\n paste (e) {\n this.resize(e)\n if (e.clipboardData.files.length > 0) {\n // prevent pasting of file as text\n e.preventDefault()\n // Strangely, files property gets emptied after event propagation\n // Trying to wrap it in array doesn't work. Plus I doubt it's possible\n // to hold more than one file in clipboard.\n this.dropFiles = [e.clipboardData.files[0]]\n }\n },\n fileDrop (e) {\n if (e.dataTransfer.files.length > 0) {\n e.preventDefault() // allow dropping text like before\n this.dropFiles = e.dataTransfer.files\n }\n },\n fileDrag (e) {\n e.dataTransfer.dropEffect = 'copy'\n },\n onEmojiInputInput (e) {\n this.$nextTick(() => {\n this.resize(this.$refs['textarea'])\n })\n },\n resize (e) {\n const target = e.target || e\n if (!(target instanceof window.Element)) { return }\n\n // Reset to default height for empty form, nothing else to do here.\n if (target.value === '') {\n target.style.height = null\n this.$refs['emoji-input'].resize()\n return\n }\n\n const formRef = this.$refs['form']\n const bottomRef = this.$refs['bottom']\n /* Scroller is either `window` (replies in TL), sidebar (main post form,\n * replies in notifs) or mobile post form. Note that getting and setting\n * scroll is different for `Window` and `Element`s\n */\n const bottomBottomPaddingStr = window.getComputedStyle(bottomRef)['padding-bottom']\n const bottomBottomPadding = Number(bottomBottomPaddingStr.substring(0, bottomBottomPaddingStr.length - 2))\n\n const scrollerRef = this.$el.closest('.sidebar-scroller') ||\n this.$el.closest('.post-form-modal-view') ||\n window\n\n // Getting info about padding we have to account for, removing 'px' part\n const topPaddingStr = window.getComputedStyle(target)['padding-top']\n const bottomPaddingStr = window.getComputedStyle(target)['padding-bottom']\n const topPadding = Number(topPaddingStr.substring(0, topPaddingStr.length - 2))\n const bottomPadding = Number(bottomPaddingStr.substring(0, bottomPaddingStr.length - 2))\n const vertPadding = topPadding + bottomPadding\n\n /* Explanation:\n *\n * https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight\n * scrollHeight returns element's scrollable content height, i.e. visible\n * element + overscrolled parts of it. We use it to determine when text\n * inside the textarea exceeded its height, so we can set height to prevent\n * overscroll, i.e. make textarea grow with the text. HOWEVER, since we\n * explicitly set new height, scrollHeight won't go below that, so we can't\n * SHRINK the textarea when there's extra space. To workaround that we set\n * height to 'auto' which makes textarea tiny again, so that scrollHeight\n * will match text height again. HOWEVER, shrinking textarea can screw with\n * the scroll since there might be not enough padding around form-bottom to even\n * warrant a scroll, so it will jump to 0 and refuse to move anywhere,\n * so we check current scroll position before shrinking and then restore it\n * with needed delta.\n */\n\n // this part has to be BEFORE the content size update\n const currentScroll = scrollerRef === window\n ? scrollerRef.scrollY\n : scrollerRef.scrollTop\n const scrollerHeight = scrollerRef === window\n ? scrollerRef.innerHeight\n : scrollerRef.offsetHeight\n const scrollerBottomBorder = currentScroll + scrollerHeight\n\n // BEGIN content size update\n target.style.height = 'auto'\n const newHeight = target.scrollHeight - vertPadding\n target.style.height = `${newHeight}px`\n // END content size update\n\n // We check where the bottom border of form-bottom element is, this uses findOffset\n // to find offset relative to scrollable container (scroller)\n const bottomBottomBorder = bottomRef.offsetHeight + findOffset(bottomRef, scrollerRef).top + bottomBottomPadding\n\n const isBottomObstructed = scrollerBottomBorder < bottomBottomBorder\n const isFormBiggerThanScroller = scrollerHeight < formRef.offsetHeight\n const bottomChangeDelta = bottomBottomBorder - scrollerBottomBorder\n // The intention is basically this;\n // Keep form-bottom always visible so that submit button is in view EXCEPT\n // if form element bigger than scroller and caret isn't at the end, so that\n // if you scroll up and edit middle of text you won't get scrolled back to bottom\n const shouldScrollToBottom = isBottomObstructed &&\n !(isFormBiggerThanScroller &&\n this.$refs.textarea.selectionStart !== this.$refs.textarea.value.length)\n const totalDelta = shouldScrollToBottom ? bottomChangeDelta : 0\n const targetScroll = currentScroll + totalDelta\n\n if (scrollerRef === window) {\n scrollerRef.scroll(0, targetScroll)\n } else {\n scrollerRef.scrollTop = targetScroll\n }\n\n this.$refs['emoji-input'].resize()\n },\n showEmojiPicker () {\n this.$refs['textarea'].focus()\n this.$refs['emoji-input'].triggerShowPicker()\n },\n clearError () {\n this.error = null\n },\n changeVis (visibility) {\n this.newStatus.visibility = visibility\n },\n togglePollForm () {\n this.pollFormVisible = !this.pollFormVisible\n },\n setPoll (poll) {\n this.newStatus.poll = poll\n },\n clearPollForm () {\n if (this.$refs.pollForm) {\n this.$refs.pollForm.clear()\n }\n },\n dismissScopeNotice () {\n this.$store.dispatch('setOption', { name: 'hideScopeNotice', value: true })\n }\n }\n}\n\nexport default PostStatusForm\n","function injectStyle (context) {\n require(\"!!vue-style-loader!css-loader?minimize!../../../node_modules/vue-loader/lib/style-compiler/index?{\\\"optionsId\\\":\\\"0\\\",\\\"vue\\\":true,\\\"scoped\\\":false,\\\"sourceMap\\\":false}!sass-loader!../../../node_modules/vue-loader/lib/selector?type=styles&index=0!./post_status_form.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./post_status_form.js\"\nimport __vue_script__ from \"!!babel-loader!./post_status_form.js\"/* template */\nimport {render as __vue_render__, staticRenderFns as __vue_static_render_fns__} from \"!!../../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-c2ba770c\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./post_status_form.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nimport normalizeComponent from \"!../../../node_modules/vue-loader/lib/runtime/component-normalizer\"\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_render__,\n __vue_static_render_fns__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{ref:\"form\",staticClass:\"post-status-form\"},[_c('form',{attrs:{\"autocomplete\":\"off\"},on:{\"submit\":function($event){$event.preventDefault();_vm.postStatus(_vm.newStatus)}}},[_c('div',{staticClass:\"form-group\"},[(!_vm.$store.state.users.currentUser.locked && _vm.newStatus.visibility == 'private')?_c('i18n',{staticClass:\"visibility-notice\",attrs:{\"path\":\"post_status.account_not_locked_warning\",\"tag\":\"p\"}},[_c('router-link',{attrs:{\"to\":{ name: 'user-settings' }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('post_status.account_not_locked_warning_link'))+\"\\n \")])],1):_vm._e(),_vm._v(\" \"),(!_vm.hideScopeNotice && _vm.newStatus.visibility === 'public')?_c('p',{staticClass:\"visibility-notice notice-dismissible\"},[_c('span',[_vm._v(_vm._s(_vm.$t('post_status.scope_notice.public')))]),_vm._v(\" \"),_c('a',{staticClass:\"button-icon dismiss\",on:{\"click\":function($event){$event.preventDefault();_vm.dismissScopeNotice()}}},[_c('i',{staticClass:\"icon-cancel\"})])]):(!_vm.hideScopeNotice && _vm.newStatus.visibility === 'unlisted')?_c('p',{staticClass:\"visibility-notice notice-dismissible\"},[_c('span',[_vm._v(_vm._s(_vm.$t('post_status.scope_notice.unlisted')))]),_vm._v(\" \"),_c('a',{staticClass:\"button-icon dismiss\",on:{\"click\":function($event){$event.preventDefault();_vm.dismissScopeNotice()}}},[_c('i',{staticClass:\"icon-cancel\"})])]):(!_vm.hideScopeNotice && _vm.newStatus.visibility === 'private' && _vm.$store.state.users.currentUser.locked)?_c('p',{staticClass:\"visibility-notice notice-dismissible\"},[_c('span',[_vm._v(_vm._s(_vm.$t('post_status.scope_notice.private')))]),_vm._v(\" \"),_c('a',{staticClass:\"button-icon dismiss\",on:{\"click\":function($event){$event.preventDefault();_vm.dismissScopeNotice()}}},[_c('i',{staticClass:\"icon-cancel\"})])]):(_vm.newStatus.visibility === 'direct')?_c('p',{staticClass:\"visibility-notice\"},[(_vm.safeDMEnabled)?_c('span',[_vm._v(_vm._s(_vm.$t('post_status.direct_warning_to_first_only')))]):_c('span',[_vm._v(_vm._s(_vm.$t('post_status.direct_warning_to_all')))])]):_vm._e(),_vm._v(\" \"),(_vm.newStatus.spoilerText || _vm.alwaysShowSubject)?_c('EmojiInput',{staticClass:\"form-control\",attrs:{\"enable-emoji-picker\":\"\",\"suggest\":_vm.emojiSuggestor},model:{value:(_vm.newStatus.spoilerText),callback:function ($$v) {_vm.$set(_vm.newStatus, \"spoilerText\", $$v)},expression:\"newStatus.spoilerText\"}},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.newStatus.spoilerText),expression:\"newStatus.spoilerText\"}],staticClass:\"form-post-subject\",attrs:{\"type\":\"text\",\"placeholder\":_vm.$t('post_status.content_warning')},domProps:{\"value\":(_vm.newStatus.spoilerText)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.newStatus, \"spoilerText\", $event.target.value)}}})]):_vm._e(),_vm._v(\" \"),_c('EmojiInput',{ref:\"emoji-input\",staticClass:\"form-control main-input\",attrs:{\"suggest\":_vm.emojiUserSuggestor,\"enable-emoji-picker\":\"\",\"hide-emoji-button\":\"\",\"enable-sticker-picker\":\"\"},on:{\"input\":_vm.onEmojiInputInput,\"sticker-uploaded\":_vm.addMediaFile,\"sticker-upload-failed\":_vm.uploadFailed},model:{value:(_vm.newStatus.status),callback:function ($$v) {_vm.$set(_vm.newStatus, \"status\", $$v)},expression:\"newStatus.status\"}},[_c('textarea',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.newStatus.status),expression:\"newStatus.status\"}],ref:\"textarea\",staticClass:\"form-post-body\",attrs:{\"placeholder\":_vm.$t('post_status.default'),\"rows\":\"1\",\"disabled\":_vm.posting},domProps:{\"value\":(_vm.newStatus.status)},on:{\"keydown\":function($event){if(!('button' in $event)&&_vm._k($event.keyCode,\"enter\",13,$event.key,\"Enter\")){ return null; }if(!$event.metaKey){ return null; }_vm.postStatus(_vm.newStatus)},\"keyup\":function($event){if(!('button' in $event)&&_vm._k($event.keyCode,\"enter\",13,$event.key,\"Enter\")){ return null; }if(!$event.ctrlKey){ return null; }_vm.postStatus(_vm.newStatus)},\"drop\":_vm.fileDrop,\"dragover\":function($event){$event.preventDefault();return _vm.fileDrag($event)},\"input\":[function($event){if($event.target.composing){ return; }_vm.$set(_vm.newStatus, \"status\", $event.target.value)},_vm.resize],\"compositionupdate\":_vm.resize,\"paste\":_vm.paste}}),_vm._v(\" \"),(_vm.hasStatusLengthLimit)?_c('p',{staticClass:\"character-counter faint\",class:{ error: _vm.isOverLengthLimit }},[_vm._v(\"\\n \"+_vm._s(_vm.charactersLeft)+\"\\n \")]):_vm._e()]),_vm._v(\" \"),_c('div',{staticClass:\"visibility-tray\"},[_c('scope-selector',{attrs:{\"show-all\":_vm.showAllScopes,\"user-default\":_vm.userDefaultScope,\"original-scope\":_vm.copyMessageScope,\"initial-scope\":_vm.newStatus.visibility,\"on-scope-change\":_vm.changeVis}}),_vm._v(\" \"),(_vm.postFormats.length > 1)?_c('div',{staticClass:\"text-format\"},[_c('label',{staticClass:\"select\",attrs:{\"for\":\"post-content-type\"}},[_c('select',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.newStatus.contentType),expression:\"newStatus.contentType\"}],staticClass:\"form-control\",attrs:{\"id\":\"post-content-type\"},on:{\"change\":function($event){var $$selectedVal = Array.prototype.filter.call($event.target.options,function(o){return o.selected}).map(function(o){var val = \"_value\" in o ? o._value : o.value;return val}); _vm.$set(_vm.newStatus, \"contentType\", $event.target.multiple ? $$selectedVal : $$selectedVal[0])}}},_vm._l((_vm.postFormats),function(postFormat){return _c('option',{key:postFormat,domProps:{\"value\":postFormat}},[_vm._v(\"\\n \"+_vm._s(_vm.$t((\"post_status.content_type[\\\"\" + postFormat + \"\\\"]\")))+\"\\n \")])}),0),_vm._v(\" \"),_c('i',{staticClass:\"icon-down-open\"})])]):_vm._e(),_vm._v(\" \"),(_vm.postFormats.length === 1 && _vm.postFormats[0] !== 'text/plain')?_c('div',{staticClass:\"text-format\"},[_c('span',{staticClass:\"only-format\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t((\"post_status.content_type[\\\"\" + (_vm.postFormats[0]) + \"\\\"]\")))+\"\\n \")])]):_vm._e()],1)],1),_vm._v(\" \"),(_vm.pollsAvailable)?_c('poll-form',{ref:\"pollForm\",attrs:{\"visible\":_vm.pollFormVisible},on:{\"update-poll\":_vm.setPoll}}):_vm._e(),_vm._v(\" \"),_c('div',{ref:\"bottom\",staticClass:\"form-bottom\"},[_c('div',{staticClass:\"form-bottom-left\"},[_c('media-upload',{ref:\"mediaUpload\",staticClass:\"media-upload-icon\",attrs:{\"drop-files\":_vm.dropFiles},on:{\"uploading\":_vm.disableSubmit,\"uploaded\":_vm.addMediaFile,\"upload-failed\":_vm.uploadFailed}}),_vm._v(\" \"),_c('div',{staticClass:\"emoji-icon\"},[_c('i',{staticClass:\"icon-smile btn btn-default\",attrs:{\"title\":_vm.$t('emoji.add_emoji')},on:{\"click\":_vm.showEmojiPicker}})]),_vm._v(\" \"),(_vm.pollsAvailable)?_c('div',{staticClass:\"poll-icon\",class:{ selected: _vm.pollFormVisible }},[_c('i',{staticClass:\"icon-chart-bar btn btn-default\",attrs:{\"title\":_vm.$t('polls.add_poll')},on:{\"click\":_vm.togglePollForm}})]):_vm._e()],1),_vm._v(\" \"),(_vm.posting)?_c('button',{staticClass:\"btn btn-default\",attrs:{\"disabled\":\"\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('post_status.posting'))+\"\\n \")]):(_vm.isOverLengthLimit)?_c('button',{staticClass:\"btn btn-default\",attrs:{\"disabled\":\"\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.submit'))+\"\\n \")]):_c('button',{staticClass:\"btn btn-default\",attrs:{\"disabled\":_vm.submitDisabled,\"type\":\"submit\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.submit'))+\"\\n \")])]),_vm._v(\" \"),(_vm.error)?_c('div',{staticClass:\"alert error\"},[_vm._v(\"\\n Error: \"+_vm._s(_vm.error)+\"\\n \"),_c('i',{staticClass:\"button-icon icon-cancel\",on:{\"click\":_vm.clearError}})]):_vm._e(),_vm._v(\" \"),_c('div',{staticClass:\"attachments\"},_vm._l((_vm.newStatus.files),function(file){return _c('div',{key:file.url,staticClass:\"media-upload-wrapper\"},[_c('i',{staticClass:\"fa button-icon icon-cancel\",on:{\"click\":function($event){_vm.removeMediaFile(file)}}}),_vm._v(\" \"),_c('div',{staticClass:\"media-upload-container attachment\"},[(_vm.type(file) === 'image')?_c('img',{staticClass:\"thumbnail media-upload\",attrs:{\"src\":file.url}}):_vm._e(),_vm._v(\" \"),(_vm.type(file) === 'video')?_c('video',{attrs:{\"src\":file.url,\"controls\":\"\"}}):_vm._e(),_vm._v(\" \"),(_vm.type(file) === 'audio')?_c('audio',{attrs:{\"src\":file.url,\"controls\":\"\"}}):_vm._e(),_vm._v(\" \"),(_vm.type(file) === 'unknown')?_c('a',{attrs:{\"href\":file.url}},[_vm._v(_vm._s(file.url))]):_vm._e()])])}),0),_vm._v(\" \"),(_vm.newStatus.files.length > 0)?_c('div',{staticClass:\"upload_settings\"},[_c('Checkbox',{model:{value:(_vm.newStatus.nsfw),callback:function ($$v) {_vm.$set(_vm.newStatus, \"nsfw\", $$v)},expression:\"newStatus.nsfw\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('post_status.attachments_sensitive'))+\"\\n \")])],1):_vm._e()],1)])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","const StillImage = {\n props: [\n 'src',\n 'referrerpolicy',\n 'mimetype',\n 'imageLoadError',\n 'imageLoadHandler'\n ],\n data () {\n return {\n stopGifs: this.$store.getters.mergedConfig.stopGifs\n }\n },\n computed: {\n animated () {\n return this.stopGifs && (this.mimetype === 'image/gif' || this.src.endsWith('.gif'))\n }\n },\n methods: {\n onLoad () {\n this.imageLoadHandler && this.imageLoadHandler(this.$refs.src)\n const canvas = this.$refs.canvas\n if (!canvas) return\n const width = this.$refs.src.naturalWidth\n const height = this.$refs.src.naturalHeight\n canvas.width = width\n canvas.height = height\n canvas.getContext('2d').drawImage(this.$refs.src, 0, 0, width, height)\n },\n onError () {\n this.imageLoadError && this.imageLoadError()\n }\n }\n}\n\nexport default StillImage\n","function injectStyle (context) {\n require(\"!!vue-style-loader!css-loader?minimize!../../../node_modules/vue-loader/lib/style-compiler/index?{\\\"optionsId\\\":\\\"0\\\",\\\"vue\\\":true,\\\"scoped\\\":false,\\\"sourceMap\\\":false}!sass-loader!../../../node_modules/vue-loader/lib/selector?type=styles&index=0!./still-image.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./still-image.js\"\nimport __vue_script__ from \"!!babel-loader!./still-image.js\"/* template */\nimport {render as __vue_render__, staticRenderFns as __vue_static_render_fns__} from \"!!../../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-1bc509fc\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./still-image.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nimport normalizeComponent from \"!../../../node_modules/vue-loader/lib/runtime/component-normalizer\"\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_render__,\n __vue_static_render_fns__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"still-image\",class:{ animated: _vm.animated }},[(_vm.animated)?_c('canvas',{ref:\"canvas\"}):_vm._e(),_vm._v(\" \"),_c('img',{key:_vm.src,ref:\"src\",attrs:{\"src\":_vm.src,\"referrerpolicy\":_vm.referrerpolicy},on:{\"load\":_vm.onLoad,\"error\":_vm.onError}})])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","\n\n\n","/* script */\nexport * from \"!!babel-loader!../../../node_modules/vue-loader/lib/selector?type=script&index=0!./timeago.vue\"\nimport __vue_script__ from \"!!babel-loader!../../../node_modules/vue-loader/lib/selector?type=script&index=0!./timeago.vue\"\n/* template */\nimport {render as __vue_render__, staticRenderFns as __vue_static_render_fns__} from \"!!../../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-ac499830\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./timeago.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = null\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nimport normalizeComponent from \"!../../../node_modules/vue-loader/lib/runtime/component-normalizer\"\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_render__,\n __vue_static_render_fns__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('time',{attrs:{\"datetime\":_vm.time,\"title\":_vm.localeDateString}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(_vm.relativeTime.key, [_vm.relativeTime.num]))+\"\\n\")])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","const fileSizeFormat = (num) => {\n var exponent\n var unit\n var units = ['B', 'KiB', 'MiB', 'GiB', 'TiB']\n if (num < 1) {\n return num + ' ' + units[0]\n }\n\n exponent = Math.min(Math.floor(Math.log(num) / Math.log(1024)), units.length - 1)\n num = (num / Math.pow(1024, exponent)).toFixed(2) * 1\n unit = units[exponent]\n return { num: num, unit: unit }\n}\nconst fileSizeFormatService = {\n fileSizeFormat\n}\nexport default fileSizeFormatService\n","import { debounce } from 'lodash'\n/**\n * suggest - generates a suggestor function to be used by emoji-input\n * data: object providing source information for specific types of suggestions:\n * data.emoji - optional, an array of all emoji available i.e.\n * (state.instance.emoji + state.instance.customEmoji)\n * data.users - optional, an array of all known users\n * updateUsersList - optional, a function to search and append to users\n *\n * Depending on data present one or both (or none) can be present, so if field\n * doesn't support user linking you can just provide only emoji.\n */\n\nconst debounceUserSearch = debounce((data, input) => {\n data.updateUsersList(input)\n}, 500, { leading: true, trailing: false })\n\nexport default data => input => {\n const firstChar = input[0]\n if (firstChar === ':' && data.emoji) {\n return suggestEmoji(data.emoji)(input)\n }\n if (firstChar === '@' && data.users) {\n return suggestUsers(data)(input)\n }\n return []\n}\n\nexport const suggestEmoji = emojis => input => {\n const noPrefix = input.toLowerCase().substr(1)\n return emojis\n .filter(({ displayText }) => displayText.toLowerCase().match(noPrefix))\n .sort((a, b) => {\n let aScore = 0\n let bScore = 0\n\n // An exact match always wins\n aScore += a.displayText.toLowerCase() === noPrefix ? 200 : 0\n bScore += b.displayText.toLowerCase() === noPrefix ? 200 : 0\n\n // Prioritize custom emoji a lot\n aScore += a.imageUrl ? 100 : 0\n bScore += b.imageUrl ? 100 : 0\n\n // Prioritize prefix matches somewhat\n aScore += a.displayText.toLowerCase().startsWith(noPrefix) ? 10 : 0\n bScore += b.displayText.toLowerCase().startsWith(noPrefix) ? 10 : 0\n\n // Sort by length\n aScore -= a.displayText.length\n bScore -= b.displayText.length\n\n // Break ties alphabetically\n const alphabetically = a.displayText > b.displayText ? 0.5 : -0.5\n\n return bScore - aScore + alphabetically\n })\n}\n\nexport const suggestUsers = data => input => {\n const noPrefix = input.toLowerCase().substr(1)\n const users = data.users\n\n const newUsers = users.filter(\n user =>\n user.screen_name.toLowerCase().startsWith(noPrefix) ||\n user.name.toLowerCase().startsWith(noPrefix)\n\n /* taking only 20 results so that sorting is a bit cheaper, we display\n * only 5 anyway. could be inaccurate, but we ideally we should query\n * backend anyway\n */\n ).slice(0, 20).sort((a, b) => {\n let aScore = 0\n let bScore = 0\n\n // Matches on screen name (i.e. user@instance) makes a priority\n aScore += a.screen_name.toLowerCase().startsWith(noPrefix) ? 2 : 0\n bScore += b.screen_name.toLowerCase().startsWith(noPrefix) ? 2 : 0\n\n // Matches on name takes second priority\n aScore += a.name.toLowerCase().startsWith(noPrefix) ? 1 : 0\n bScore += b.name.toLowerCase().startsWith(noPrefix) ? 1 : 0\n\n const diff = (bScore - aScore) * 10\n\n // Then sort alphabetically\n const nameAlphabetically = a.name > b.name ? 1 : -1\n const screenNameAlphabetically = a.screen_name > b.screen_name ? 1 : -1\n\n return diff + nameAlphabetically + screenNameAlphabetically\n /* eslint-disable camelcase */\n }).map(({ screen_name, name, profile_image_url_original }) => ({\n displayText: screen_name,\n detailText: name,\n imageUrl: profile_image_url_original,\n replacement: '@' + screen_name + ' '\n }))\n\n // BE search users if there are no matches\n if (newUsers.length === 0 && data.updateUsersList) {\n debounceUserSearch(data, noPrefix)\n }\n return newUsers\n /* eslint-enable camelcase */\n}\n","import { map } from 'lodash'\nimport apiService from '../api/api.service.js'\n\nconst postStatus = ({ store, status, spoilerText, visibility, sensitive, poll, media = [], inReplyToStatusId = undefined, contentType = 'text/plain' }) => {\n const mediaIds = map(media, 'id')\n\n return apiService.postStatus({\n credentials: store.state.users.currentUser.credentials,\n status,\n spoilerText,\n visibility,\n sensitive,\n mediaIds,\n inReplyToStatusId,\n contentType,\n poll })\n .then((data) => {\n if (!data.error) {\n store.dispatch('addNewStatuses', {\n statuses: [data],\n timeline: 'friends',\n showImmediately: true,\n noIdUpdate: true // To prevent missing notices on next pull.\n })\n }\n return data\n })\n .catch((err) => {\n return {\n error: err.message\n }\n })\n}\n\nconst uploadMedia = ({ store, formData }) => {\n const credentials = store.state.users.currentUser.credentials\n\n return apiService.uploadMedia({ credentials, formData })\n}\n\nconst statusPosterService = {\n postStatus,\n uploadMedia\n}\n\nexport default statusPosterService\n","export const findOffset = (child, parent, { top = 0, left = 0 } = {}, ignorePadding = true) => {\n const result = {\n top: top + child.offsetTop,\n left: left + child.offsetLeft\n }\n if (!ignorePadding && child !== window) {\n const { topPadding, leftPadding } = findPadding(child)\n result.top += ignorePadding ? 0 : topPadding\n result.left += ignorePadding ? 0 : leftPadding\n }\n\n if (child.offsetParent && (parent === window || parent.contains(child.offsetParent) || parent === child.offsetParent)) {\n return findOffset(child.offsetParent, parent, result, false)\n } else {\n if (parent !== window) {\n const { topPadding, leftPadding } = findPadding(parent)\n result.top += topPadding\n result.left += leftPadding\n }\n return result\n }\n}\n\nconst findPadding = (el) => {\n const topPaddingStr = window.getComputedStyle(el)['padding-top']\n const topPadding = Number(topPaddingStr.substring(0, topPaddingStr.length - 2))\n const leftPaddingStr = window.getComputedStyle(el)['padding-left']\n const leftPadding = Number(leftPaddingStr.substring(0, leftPaddingStr.length - 2))\n\n return { topPadding, leftPadding }\n}\n","import { reduce, find } from 'lodash'\n\nexport const replaceWord = (str, toReplace, replacement) => {\n return str.slice(0, toReplace.start) + replacement + str.slice(toReplace.end)\n}\n\nexport const wordAtPosition = (str, pos) => {\n const words = splitIntoWords(str)\n const wordsWithPosition = addPositionToWords(words)\n\n return find(wordsWithPosition, ({ start, end }) => start <= pos && end > pos)\n}\n\nexport const addPositionToWords = (words) => {\n return reduce(words, (result, word) => {\n const data = {\n word,\n start: 0,\n end: word.length\n }\n\n if (result.length > 0) {\n const previous = result.pop()\n\n data.start += previous.end\n data.end += previous.end\n\n result.push(previous)\n }\n\n result.push(data)\n\n return result\n }, [])\n}\n\nexport const splitIntoWords = (str) => {\n // Split at word boundaries\n const regex = /\\b/\n const triggers = /[@#:]+$/\n\n let split = str.split(regex)\n\n // Add trailing @ and # to the following word.\n const words = reduce(split, (result, word) => {\n if (result.length > 0) {\n let previous = result.pop()\n const matches = previous.match(triggers)\n if (matches) {\n previous = previous.replace(triggers, '')\n word = matches[0] + word\n }\n result.push(previous)\n }\n result.push(word)\n\n return result\n }, [])\n\n return words\n}\n\nconst completion = {\n wordAtPosition,\n addPositionToWords,\n splitIntoWords,\n replaceWord\n}\n\nexport default completion\n","import Checkbox from '../checkbox/checkbox.vue'\n\n// At widest, approximately 20 emoji are visible in a row,\n// loading 3 rows, could be overkill for narrow picker\nconst LOAD_EMOJI_BY = 60\n\n// When to start loading new batch emoji, in pixels\nconst LOAD_EMOJI_MARGIN = 64\n\nconst filterByKeyword = (list, keyword = '') => {\n return list.filter(x => x.displayText.includes(keyword))\n}\n\nconst EmojiPicker = {\n props: {\n enableStickerPicker: {\n required: false,\n type: Boolean,\n default: false\n }\n },\n data () {\n return {\n keyword: '',\n activeGroup: 'custom',\n showingStickers: false,\n groupsScrolledClass: 'scrolled-top',\n keepOpen: false,\n customEmojiBufferSlice: LOAD_EMOJI_BY,\n customEmojiTimeout: null,\n customEmojiLoadAllConfirmed: false\n }\n },\n components: {\n StickerPicker: () => import('../sticker_picker/sticker_picker.vue'),\n Checkbox\n },\n methods: {\n onStickerUploaded (e) {\n this.$emit('sticker-uploaded', e)\n },\n onStickerUploadFailed (e) {\n this.$emit('sticker-upload-failed', e)\n },\n onEmoji (emoji) {\n const value = emoji.imageUrl ? `:${emoji.displayText}:` : emoji.replacement\n this.$emit('emoji', { insertion: value, keepOpen: this.keepOpen })\n },\n onScroll (e) {\n const target = (e && e.target) || this.$refs['emoji-groups']\n this.updateScrolledClass(target)\n this.scrolledGroup(target)\n this.triggerLoadMore(target)\n },\n highlight (key) {\n const ref = this.$refs['group-' + key]\n const top = ref[0].offsetTop\n this.setShowStickers(false)\n this.activeGroup = key\n this.$nextTick(() => {\n this.$refs['emoji-groups'].scrollTop = top + 1\n })\n },\n updateScrolledClass (target) {\n if (target.scrollTop <= 5) {\n this.groupsScrolledClass = 'scrolled-top'\n } else if (target.scrollTop >= target.scrollTopMax - 5) {\n this.groupsScrolledClass = 'scrolled-bottom'\n } else {\n this.groupsScrolledClass = 'scrolled-middle'\n }\n },\n triggerLoadMore (target) {\n const ref = this.$refs['group-end-custom'][0]\n if (!ref) return\n const bottom = ref.offsetTop + ref.offsetHeight\n\n const scrollerBottom = target.scrollTop + target.clientHeight\n const scrollerTop = target.scrollTop\n const scrollerMax = target.scrollHeight\n\n // Loads more emoji when they come into view\n const approachingBottom = bottom - scrollerBottom < LOAD_EMOJI_MARGIN\n // Always load when at the very top in case there's no scroll space yet\n const atTop = scrollerTop < 5\n // Don't load when looking at unicode category or at the very bottom\n const bottomAboveViewport = bottom < scrollerTop || scrollerBottom === scrollerMax\n if (!bottomAboveViewport && (approachingBottom || atTop)) {\n this.loadEmoji()\n }\n },\n scrolledGroup (target) {\n const top = target.scrollTop + 5\n this.$nextTick(() => {\n this.emojisView.forEach(group => {\n const ref = this.$refs['group-' + group.id]\n if (ref[0].offsetTop <= top) {\n this.activeGroup = group.id\n }\n })\n })\n },\n loadEmoji () {\n const allLoaded = this.customEmojiBuffer.length === this.filteredEmoji.length\n\n if (allLoaded) {\n return\n }\n\n this.customEmojiBufferSlice += LOAD_EMOJI_BY\n },\n startEmojiLoad (forceUpdate = false) {\n if (!forceUpdate) {\n this.keyword = ''\n }\n this.$nextTick(() => {\n this.$refs['emoji-groups'].scrollTop = 0\n })\n const bufferSize = this.customEmojiBuffer.length\n const bufferPrefilledAll = bufferSize === this.filteredEmoji.length\n if (bufferPrefilledAll && !forceUpdate) {\n return\n }\n this.customEmojiBufferSlice = LOAD_EMOJI_BY\n },\n toggleStickers () {\n this.showingStickers = !this.showingStickers\n },\n setShowStickers (value) {\n this.showingStickers = value\n }\n },\n watch: {\n keyword () {\n this.customEmojiLoadAllConfirmed = false\n this.onScroll()\n this.startEmojiLoad(true)\n }\n },\n computed: {\n activeGroupView () {\n return this.showingStickers ? '' : this.activeGroup\n },\n stickersAvailable () {\n if (this.$store.state.instance.stickers) {\n return this.$store.state.instance.stickers.length > 0\n }\n return 0\n },\n filteredEmoji () {\n return filterByKeyword(\n this.$store.state.instance.customEmoji || [],\n this.keyword\n )\n },\n customEmojiBuffer () {\n return this.filteredEmoji.slice(0, this.customEmojiBufferSlice)\n },\n emojis () {\n const standardEmojis = this.$store.state.instance.emoji || []\n const customEmojis = this.customEmojiBuffer\n\n return [\n {\n id: 'custom',\n text: this.$t('emoji.custom'),\n icon: 'icon-smile',\n emojis: customEmojis\n },\n {\n id: 'standard',\n text: this.$t('emoji.unicode'),\n icon: 'icon-picture',\n emojis: filterByKeyword(standardEmojis, this.keyword)\n }\n ]\n },\n emojisView () {\n return this.emojis.filter(value => value.emojis.length > 0)\n },\n stickerPickerEnabled () {\n return (this.$store.state.instance.stickers || []).length !== 0\n }\n }\n}\n\nexport default EmojiPicker\n","function injectStyle (context) {\n require(\"!!vue-style-loader!css-loader?minimize!../../../node_modules/vue-loader/lib/style-compiler/index?{\\\"optionsId\\\":\\\"0\\\",\\\"vue\\\":true,\\\"scoped\\\":false,\\\"sourceMap\\\":false}!sass-loader!./emoji_picker.scss\")\n}\n/* script */\nexport * from \"!!babel-loader!./emoji_picker.js\"\nimport __vue_script__ from \"!!babel-loader!./emoji_picker.js\"/* template */\nimport {render as __vue_render__, staticRenderFns as __vue_static_render_fns__} from \"!!../../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-47d21b3b\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./emoji_picker.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nimport normalizeComponent from \"!../../../node_modules/vue-loader/lib/runtime/component-normalizer\"\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_render__,\n __vue_static_render_fns__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"emoji-picker panel panel-default panel-body\"},[_c('div',{staticClass:\"heading\"},[_c('span',{staticClass:\"emoji-tabs\"},_vm._l((_vm.emojis),function(group){return _c('span',{key:group.id,staticClass:\"emoji-tabs-item\",class:{\n active: _vm.activeGroupView === group.id,\n disabled: group.emojis.length === 0\n },attrs:{\"title\":group.text},on:{\"click\":function($event){$event.preventDefault();_vm.highlight(group.id)}}},[_c('i',{class:group.icon})])}),0),_vm._v(\" \"),(_vm.stickerPickerEnabled)?_c('span',{staticClass:\"additional-tabs\"},[_c('span',{staticClass:\"stickers-tab-icon additional-tabs-item\",class:{active: _vm.showingStickers},attrs:{\"title\":_vm.$t('emoji.stickers')},on:{\"click\":function($event){$event.preventDefault();return _vm.toggleStickers($event)}}},[_c('i',{staticClass:\"icon-star\"})])]):_vm._e()]),_vm._v(\" \"),_c('div',{staticClass:\"content\"},[_c('div',{staticClass:\"emoji-content\",class:{hidden: _vm.showingStickers}},[_c('div',{staticClass:\"emoji-search\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.keyword),expression:\"keyword\"}],staticClass:\"form-control\",attrs:{\"type\":\"text\",\"placeholder\":_vm.$t('emoji.search_emoji')},domProps:{\"value\":(_vm.keyword)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.keyword=$event.target.value}}})]),_vm._v(\" \"),_c('div',{ref:\"emoji-groups\",staticClass:\"emoji-groups\",class:_vm.groupsScrolledClass,on:{\"scroll\":_vm.onScroll}},_vm._l((_vm.emojisView),function(group){return _c('div',{key:group.id,staticClass:\"emoji-group\"},[_c('h6',{ref:'group-' + group.id,refInFor:true,staticClass:\"emoji-group-title\"},[_vm._v(\"\\n \"+_vm._s(group.text)+\"\\n \")]),_vm._v(\" \"),_vm._l((group.emojis),function(emoji){return _c('span',{key:group.id + emoji.displayText,staticClass:\"emoji-item\",attrs:{\"title\":emoji.displayText},on:{\"click\":function($event){$event.stopPropagation();$event.preventDefault();_vm.onEmoji(emoji)}}},[(!emoji.imageUrl)?_c('span',[_vm._v(_vm._s(emoji.replacement))]):_c('img',{attrs:{\"src\":emoji.imageUrl}})])}),_vm._v(\" \"),_c('span',{ref:'group-end-' + group.id,refInFor:true})],2)}),0),_vm._v(\" \"),_c('div',{staticClass:\"keep-open\"},[_c('Checkbox',{model:{value:(_vm.keepOpen),callback:function ($$v) {_vm.keepOpen=$$v},expression:\"keepOpen\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('emoji.keep_open'))+\"\\n \")])],1)]),_vm._v(\" \"),(_vm.showingStickers)?_c('div',{staticClass:\"stickers-content\"},[_c('sticker-picker',{on:{\"uploaded\":_vm.onStickerUploaded,\"upload-failed\":_vm.onStickerUploadFailed}})],1):_vm._e()])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","import Completion from '../../services/completion/completion.js'\nimport EmojiPicker from '../emoji_picker/emoji_picker.vue'\nimport { take } from 'lodash'\nimport { findOffset } from '../../services/offset_finder/offset_finder.service.js'\n\n/**\n * EmojiInput - augmented inputs for emoji and autocomplete support in inputs\n * without having to give up the comfort of and