From 45e21a9df4a7b58d28624534fdde3dc9e31c6813 Mon Sep 17 00:00:00 2001 From: kPherox Date: Sun, 25 Aug 2019 06:51:05 +0900 Subject: [PATCH 01/37] Rename fields to fields_attributes --- lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 4 ++-- .../mastodon_api_controller/update_credentials_test.exs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 53cf95fbb..98b2e75f3 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -159,12 +159,12 @@ def update_credentials(%{assigns: %{user: user}} = conn, params) do end) end) |> add_if_present(params, "default_scope", :default_scope) - |> add_if_present(params, "fields", :fields, fn fields -> + |> add_if_present(params, "fields_attributes", :fields, fn fields -> fields = Enum.map(fields, fn f -> Map.update!(f, "value", &AutoLinker.link(&1)) end) {:ok, fields} end) - |> add_if_present(params, "fields", :raw_fields) + |> add_if_present(params, "fields_attributes", :raw_fields) |> add_if_present(params, "pleroma_settings_store", :pleroma_settings_store, fn value -> {:ok, Map.merge(user.info.pleroma_settings_store, value)} end) diff --git a/test/web/mastodon_api/mastodon_api_controller/update_credentials_test.exs b/test/web/mastodon_api/mastodon_api_controller/update_credentials_test.exs index 87ee82050..b1a5c2aea 100644 --- a/test/web/mastodon_api/mastodon_api_controller/update_credentials_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller/update_credentials_test.exs @@ -313,7 +313,7 @@ test "update fields", %{conn: conn} do account = conn |> assign(:user, user) - |> patch("/api/v1/accounts/update_credentials", %{"fields" => fields}) + |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields}) |> json_response(200) assert account["fields"] == [ @@ -339,7 +339,7 @@ test "update fields", %{conn: conn} do assert %{"error" => "Invalid request"} == conn |> assign(:user, user) - |> patch("/api/v1/accounts/update_credentials", %{"fields" => fields}) + |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields}) |> json_response(403) long_name = Enum.map(0..name_limit, fn _ -> "x" end) |> Enum.join() @@ -349,7 +349,7 @@ test "update fields", %{conn: conn} do assert %{"error" => "Invalid request"} == conn |> assign(:user, user) - |> patch("/api/v1/accounts/update_credentials", %{"fields" => fields}) + |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields}) |> json_response(403) Pleroma.Config.put([:instance, :max_account_fields], 1) @@ -362,7 +362,7 @@ test "update fields", %{conn: conn} do assert %{"error" => "Invalid request"} == conn |> assign(:user, user) - |> patch("/api/v1/accounts/update_credentials", %{"fields" => fields}) + |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields}) |> json_response(403) end end From 705b5adfc436b18ab3d1b8ff94274d9a2a6a6912 Mon Sep 17 00:00:00 2001 From: kPherox Date: Sun, 25 Aug 2019 07:02:32 +0900 Subject: [PATCH 02/37] Fix type of fields_attributes Convert tuple list to map list when parameters is `:urlencoded` or `:multipart` --- lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 98b2e75f3..2826cee8c 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -143,6 +143,13 @@ def update_credentials(%{assigns: %{user: user}} = conn, params) do |> Enum.concat(Formatter.get_emoji_map(emojis_text)) |> Enum.dedup() + params = + if Map.has_key?(params, "fields_attributes") && Enum.all?(params["fields_attributes"], &is_tuple/1) do + Map.update!(params, "fields_attributes", &Enum.map(&1, fn {_, v} -> v end)) + else + params + end + info_params = [ :no_rich_text, From b15e226593d4d9d58898af5576d2a7e96bed59ae Mon Sep 17 00:00:00 2001 From: kPherox Date: Sun, 25 Aug 2019 07:04:46 +0900 Subject: [PATCH 03/37] Change to delete empty name field --- .../web/mastodon_api/mastodon_api_controller.ex | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 2826cee8c..ca2230630 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -144,8 +144,15 @@ def update_credentials(%{assigns: %{user: user}} = conn, params) do |> Enum.dedup() params = - if Map.has_key?(params, "fields_attributes") && Enum.all?(params["fields_attributes"], &is_tuple/1) do - Map.update!(params, "fields_attributes", &Enum.map(&1, fn {_, v} -> v end)) + if Map.has_key?(params, "fields_attributes") do + Map.update!(params, "fields_attributes", fn fields -> + if Enum.all?(fields, &is_tuple/1) do + Enum.map(fields, fn {_, v} -> v end) + else + fields + end + |> Enum.filter(fn %{"name" => n} -> n != "" end) + end) else params end From b8777b01aeb4656e74437c1ac4916d2fcbe7f39e Mon Sep 17 00:00:00 2001 From: kPherox Date: Mon, 26 Aug 2019 03:25:06 +0900 Subject: [PATCH 04/37] Update test for custom fields when name empty string --- .../update_credentials_test.exs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/web/mastodon_api/mastodon_api_controller/update_credentials_test.exs b/test/web/mastodon_api/mastodon_api_controller/update_credentials_test.exs index b1a5c2aea..21dae98fa 100644 --- a/test/web/mastodon_api/mastodon_api_controller/update_credentials_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller/update_credentials_test.exs @@ -364,6 +364,21 @@ test "update fields", %{conn: conn} do |> assign(:user, user) |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields}) |> json_response(403) + + fields = [ + %{"name" => "foo", "value" => ""}, + %{"name" => "", "value" => "bar"} + ] + + account = + conn + |> assign(:user, user) + |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields}) + |> json_response(200) + + assert account["fields"] == [ + %{"name" => "foo", "value" => ""} + ] end end end From eb75ea502d9c608ef892feeda02fa49578e9c369 Mon Sep 17 00:00:00 2001 From: kPherox Date: Mon, 26 Aug 2019 03:49:47 +0900 Subject: [PATCH 05/37] Update test for custom fields when content-type urlencoded --- .../update_credentials_test.exs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/test/web/mastodon_api/mastodon_api_controller/update_credentials_test.exs b/test/web/mastodon_api/mastodon_api_controller/update_credentials_test.exs index 21dae98fa..56a8f1716 100644 --- a/test/web/mastodon_api/mastodon_api_controller/update_credentials_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller/update_credentials_test.exs @@ -329,6 +329,35 @@ test "update fields", %{conn: conn} do %{"name" => "link", "value" => "cofe.io"} ] + fields = + [ + "fields_attributes[1][name]=link", + "fields_attributes[1][value]=cofe.io", + "fields_attributes[0][name]=foo", + "fields_attributes[0][value]=bar" + ] + |> Enum.join("&") + + account = + conn + |> put_req_header("content-type", "application/x-www-form-urlencoded") + |> assign(:user, user) + |> patch("/api/v1/accounts/update_credentials", fields) + |> json_response(200) + + assert account["fields"] == [ + %{"name" => "foo", "value" => "bar"}, + %{"name" => "link", "value" => "cofe.io"} + ] + + assert account["source"]["fields"] == [ + %{ + "name" => "foo", + "value" => "bar" + }, + %{"name" => "link", "value" => "cofe.io"} + ] + name_limit = Pleroma.Config.get([:instance, :account_field_name_length]) value_limit = Pleroma.Config.get([:instance, :account_field_value_length]) From 8ca4f145a51e92c9f3a6c374ceddfac22ea300d9 Mon Sep 17 00:00:00 2001 From: kPherox Date: Mon, 26 Aug 2019 17:09:32 +0900 Subject: [PATCH 06/37] Extract if block into private function --- .../web/mastodon_api/mastodon_api_controller.ex | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index ca2230630..3ca1630f4 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -119,6 +119,14 @@ defp add_if_present( end end + defp normalize_fields_attributes(fields) do + if Enum.all?(fields, &is_tuple/1) do + Enum.map(fields, fn {_, v} -> v end) + else + fields + end + end + def update_credentials(%{assigns: %{user: user}} = conn, params) do original_user = user @@ -146,11 +154,8 @@ def update_credentials(%{assigns: %{user: user}} = conn, params) do params = if Map.has_key?(params, "fields_attributes") do Map.update!(params, "fields_attributes", fn fields -> - if Enum.all?(fields, &is_tuple/1) do - Enum.map(fields, fn {_, v} -> v end) - else - fields - end + fields + |> normalize_fields_attributes() |> Enum.filter(fn %{"name" => n} -> n != "" end) end) else From b63faf9819c2c49d2e9b63e7f37136eb03d8b4e8 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Sun, 8 Sep 2019 15:00:03 +0300 Subject: [PATCH 07/37] [#1234] Mastodon 2.4.3 hierarchical scopes initial support (WIP). --- lib/pleroma/plugs/oauth_scopes_plug.ex | 20 ++++++++-- .../controllers/mastodon_api_controller.ex | 36 ++++++++++++++++++ lib/pleroma/web/oauth/oauth_controller.ex | 2 +- lib/pleroma/web/oauth/scopes.ex | 14 +++---- lib/pleroma/web/router.ex | 14 +------ .../controllers/util_controller.ex | 9 +++++ test/plugs/oauth_scopes_plug_test.exs | 38 ++++++++++++++++++- test/web/twitter_api/util_controller_test.exs | 10 +++-- 8 files changed, 113 insertions(+), 30 deletions(-) diff --git a/lib/pleroma/plugs/oauth_scopes_plug.ex b/lib/pleroma/plugs/oauth_scopes_plug.ex index b508628a9..41403047e 100644 --- a/lib/pleroma/plugs/oauth_scopes_plug.ex +++ b/lib/pleroma/plugs/oauth_scopes_plug.ex @@ -13,15 +13,16 @@ def init(%{scopes: _} = options), do: options def call(%Plug.Conn{assigns: assigns} = conn, %{scopes: scopes} = options) do op = options[:op] || :| token = assigns[:token] + matched_scopes = token && filter_descendants(scopes, token.scopes) cond do is_nil(token) -> conn - op == :| && scopes -- token.scopes != scopes -> + op == :| && Enum.any?(matched_scopes) -> conn - op == :& && scopes -- token.scopes == [] -> + op == :& && matched_scopes == scopes -> conn options[:fallback] == :proceed_unauthenticated -> @@ -30,7 +31,7 @@ def call(%Plug.Conn{assigns: assigns} = conn, %{scopes: scopes} = options) do |> assign(:token, nil) true -> - missing_scopes = scopes -- token.scopes + missing_scopes = scopes -- matched_scopes permissions = Enum.join(missing_scopes, " #{op} ") error_message = @@ -42,4 +43,17 @@ def call(%Plug.Conn{assigns: assigns} = conn, %{scopes: scopes} = options) do |> halt() end end + + @doc "Filters descendants of supported scopes" + def filter_descendants(scopes, supported_scopes) do + Enum.filter( + scopes, + fn scope -> + Enum.find( + supported_scopes, + &(scope == &1 || String.starts_with?(scope, &1 <> ":")) + ) + end + ) + end end diff --git a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex index 8dfad7a54..118446c85 100644 --- a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex @@ -19,6 +19,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do alias Pleroma.Notification alias Pleroma.Object alias Pleroma.Pagination + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Plugs.RateLimiter alias Pleroma.Repo alias Pleroma.ScheduledActivity @@ -52,6 +53,41 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do require Logger require Pleroma.Constants + plug( + OAuthScopesPlug, + %{scopes: ["follow", "read:blocks"]} when action in [:blocks, :domain_blocks] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["follow", "write:blocks"]} + when action in [:block, :unblock, :block_domain, :unblock_domain] + ) + + plug(OAuthScopesPlug, %{scopes: ["follow", "read:follows"]} when action == :follow_requests) + + plug( + OAuthScopesPlug, + %{scopes: ["follow", "write:follows"]} + when action in [ + :follow, + :unfollow, + :subscribe, + :unsubscribe, + :authorize_follow_request, + :reject_follow_request + ] + ) + + plug(OAuthScopesPlug, %{scopes: ["follow", "read:mutes"]} when action == :mutes) + plug(OAuthScopesPlug, %{scopes: ["follow", "write:mutes"]} when action in [:mute, :unmute]) + + plug( + OAuthScopesPlug, + %{scopes: ["write:mutes"]} + when action in [:mute_conversation, :unmute_conversation] + ) + @rate_limited_relations_actions ~w(follow unfollow)a @rate_limited_status_actions ~w(reblog_status unreblog_status fav_status unfav_status diff --git a/lib/pleroma/web/oauth/oauth_controller.ex b/lib/pleroma/web/oauth/oauth_controller.ex index 81eae2c8b..130ec7895 100644 --- a/lib/pleroma/web/oauth/oauth_controller.ex +++ b/lib/pleroma/web/oauth/oauth_controller.ex @@ -451,7 +451,7 @@ defp put_session_registration_id(%Plug.Conn{} = conn, registration_id), defp validate_scopes(app, params) do params |> Scopes.fetch_scopes(app.scopes) - |> Scopes.validates(app.scopes) + |> Scopes.validate(app.scopes) end def default_redirect_uri(%App{} = app) do diff --git a/lib/pleroma/web/oauth/scopes.ex b/lib/pleroma/web/oauth/scopes.ex index ad9dfb260..48bd14407 100644 --- a/lib/pleroma/web/oauth/scopes.ex +++ b/lib/pleroma/web/oauth/scopes.ex @@ -8,7 +8,7 @@ defmodule Pleroma.Web.OAuth.Scopes do """ @doc """ - Fetch scopes from requiest params. + Fetch scopes from request params. Note: `scopes` is used by Mastodon — supporting it but sticking to OAuth's standard `scope` wherever we control it @@ -53,14 +53,14 @@ def to_string(scopes), do: Enum.join(scopes, " ") @doc """ Validates scopes. """ - @spec validates(list() | nil, list()) :: + @spec validate(list() | nil, list()) :: {:ok, list()} | {:error, :missing_scopes | :unsupported_scopes} - def validates([], _app_scopes), do: {:error, :missing_scopes} - def validates(nil, _app_scopes), do: {:error, :missing_scopes} + def validate([], _app_scopes), do: {:error, :missing_scopes} + def validate(nil, _app_scopes), do: {:error, :missing_scopes} - def validates(scopes, app_scopes) do - case scopes -- app_scopes do - [] -> {:ok, scopes} + def validate(scopes, app_scopes) do + case Pleroma.Plugs.OAuthScopesPlug.filter_descendants(scopes, app_scopes) do + ^scopes -> {:ok, scopes} _ -> {:error, :unsupported_scopes} end end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index cfb973f53..8c93e535e 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -104,10 +104,6 @@ defmodule Pleroma.Web.Router do plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["write"]}) end - pipeline :oauth_follow do - plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["follow"]}) - end - pipeline :oauth_push do plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["push"]}) end @@ -211,11 +207,7 @@ defmodule Pleroma.Web.Router do post("/main/ostatus", UtilController, :remote_subscribe) get("/ostatus_subscribe", UtilController, :remote_follow) - - scope [] do - pipe_through(:oauth_follow) - post("/ostatus_subscribe", UtilController, :do_remote_follow) - end + post("/ostatus_subscribe", UtilController, :do_remote_follow) end scope "/api/pleroma", Pleroma.Web.TwitterAPI do @@ -231,8 +223,6 @@ defmodule Pleroma.Web.Router do end scope [] do - pipe_through(:oauth_follow) - post("/blocks_import", UtilController, :blocks_import) post("/follow_import", UtilController, :follow_import) end @@ -373,8 +363,6 @@ defmodule Pleroma.Web.Router do end scope [] do - pipe_through(:oauth_follow) - post("/follows", MastodonAPIController, :follow) post("/accounts/:id/follow", MastodonAPIController, :follow) diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index 3405bd3b7..1c6ad5057 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -13,11 +13,20 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do alias Pleroma.Healthcheck alias Pleroma.Notification alias Pleroma.Plugs.AuthenticationPlug + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User alias Pleroma.Web alias Pleroma.Web.CommonAPI alias Pleroma.Web.WebFinger + plug( + OAuthScopesPlug, + %{scopes: ["follow", "write:follows"]} + when action in [:do_remote_follow, :follow_import] + ) + + plug(OAuthScopesPlug, %{scopes: ["follow", "write:blocks"]} when action == :blocks_import) + plug(Pleroma.Plugs.SetFormatPlug when action in [:config, :version]) def help_test(conn, _params) do diff --git a/test/plugs/oauth_scopes_plug_test.exs b/test/plugs/oauth_scopes_plug_test.exs index f328026df..9b0a2e702 100644 --- a/test/plugs/oauth_scopes_plug_test.exs +++ b/test/plugs/oauth_scopes_plug_test.exs @@ -84,7 +84,8 @@ test "proceeds with cleared `assigns[:user]` if `token.scopes` doesn't fulfill s refute conn.assigns[:user] end - test "returns 403 and halts in case of no :fallback option and `token.scopes` not fulfilling specified 'any of' conditions", + test "returns 403 and halts " <> + "in case of no :fallback option and `token.scopes` not fulfilling specified 'any of' conditions", %{conn: conn} do token = insert(:oauth_token, scopes: ["read", "write"]) any_of_scopes = ["follow"] @@ -101,7 +102,8 @@ test "returns 403 and halts in case of no :fallback option and `token.scopes` no assert Jason.encode!(%{error: expected_error}) == conn.resp_body end - test "returns 403 and halts in case of no :fallback option and `token.scopes` not fulfilling specified 'all of' conditions", + test "returns 403 and halts " <> + "in case of no :fallback option and `token.scopes` not fulfilling specified 'all of' conditions", %{conn: conn} do token = insert(:oauth_token, scopes: ["read", "write"]) all_of_scopes = ["write", "follow"] @@ -119,4 +121,36 @@ test "returns 403 and halts in case of no :fallback option and `token.scopes` no assert Jason.encode!(%{error: expected_error}) == conn.resp_body end + + describe "with hierarchical scopes, " do + test "proceeds with no op if `token.scopes` fulfill specified 'any of' conditions", %{ + conn: conn + } do + token = insert(:oauth_token, scopes: ["read", "write"]) |> Repo.preload(:user) + + conn = + conn + |> assign(:user, token.user) + |> assign(:token, token) + |> OAuthScopesPlug.call(%{scopes: ["read:something"]}) + + refute conn.halted + assert conn.assigns[:user] + end + + test "proceeds with no op if `token.scopes` fulfill specified 'all of' conditions", %{ + conn: conn + } do + token = insert(:oauth_token, scopes: ["scope1", "scope2", "scope3"]) |> Repo.preload(:user) + + conn = + conn + |> assign(:user, token.user) + |> assign(:token, token) + |> OAuthScopesPlug.call(%{scopes: ["scope1:subscope", "scope2:subscope"], op: :&}) + + refute conn.halted + assert conn.assigns[:user] + end + end end diff --git a/test/web/twitter_api/util_controller_test.exs b/test/web/twitter_api/util_controller_test.exs index cf8e69d2b..685e48270 100644 --- a/test/web/twitter_api/util_controller_test.exs +++ b/test/web/twitter_api/util_controller_test.exs @@ -78,19 +78,21 @@ test "it imports new-style mastodon follow lists", %{conn: conn} do assert response == "job started" end - test "requires 'follow' permission", %{conn: conn} do + test "requires 'follow' or 'write:follows' permissions", %{conn: conn} do token1 = insert(:oauth_token, scopes: ["read", "write"]) token2 = insert(:oauth_token, scopes: ["follow"]) + token3 = insert(:oauth_token, scopes: ["something"]) another_user = insert(:user) - for token <- [token1, token2] do + for token <- [token1, token2, token3] do conn = conn |> put_req_header("authorization", "Bearer #{token.token}") |> post("/api/pleroma/follow_import", %{"list" => "#{another_user.ap_id}"}) - if token == token1 do - assert %{"error" => "Insufficient permissions: follow."} == json_response(conn, 403) + if token == token3 do + assert %{"error" => "Insufficient permissions: follow | write:follows."} == + json_response(conn, 403) else assert json_response(conn, 200) end From e6f43a831bdd2a381ed4de493344886f312f9a38 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Sun, 15 Sep 2019 18:22:08 +0300 Subject: [PATCH 08/37] [#1234] Permissions-related fixes / new functionality (Masto 2.4.3 scopes). --- lib/pleroma/plugs/oauth_scopes_plug.ex | 12 +- .../activity_pub/activity_pub_controller.ex | 5 + .../web/admin_api/admin_api_controller.ex | 51 +++ .../controllers/list_controller.ex | 9 + .../controllers/mastodon_api_controller.ex | 135 +++++++- .../controllers/search_controller.ex | 5 + .../controllers/subscription_controller.ex | 2 + .../web/pleroma_api/pleroma_api_controller.ex | 13 + lib/pleroma/web/router.ex | 313 +++++++----------- .../controllers/util_controller.ex | 11 + .../web/twitter_api/twitter_api_controller.ex | 3 + test/support/factory.ex | 1 + .../update_credentials_test.exs | 5 +- test/web/oauth/oauth_controller_test.exs | 6 +- 14 files changed, 374 insertions(+), 197 deletions(-) diff --git a/lib/pleroma/plugs/oauth_scopes_plug.ex b/lib/pleroma/plugs/oauth_scopes_plug.ex index 41403047e..e0d61c4eb 100644 --- a/lib/pleroma/plugs/oauth_scopes_plug.ex +++ b/lib/pleroma/plugs/oauth_scopes_plug.ex @@ -6,6 +6,8 @@ defmodule Pleroma.Plugs.OAuthScopesPlug do import Plug.Conn import Pleroma.Web.Gettext + alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug + @behaviour Plug def init(%{scopes: _} = options), do: options @@ -17,7 +19,7 @@ def call(%Plug.Conn{assigns: assigns} = conn, %{scopes: scopes} = options) do cond do is_nil(token) -> - conn + maybe_perform_instance_privacy_check(conn, options) op == :| && Enum.any?(matched_scopes) -> conn @@ -29,6 +31,7 @@ def call(%Plug.Conn{assigns: assigns} = conn, %{scopes: scopes} = options) do conn |> assign(:user, nil) |> assign(:token, nil) + |> maybe_perform_instance_privacy_check(options) true -> missing_scopes = scopes -- matched_scopes @@ -56,4 +59,11 @@ def filter_descendants(scopes, supported_scopes) do end ) end + + defp maybe_perform_instance_privacy_check(%Plug.Conn{} = conn, options) do + case options[:skip_instance_privacy_check] do + true -> conn + _ -> EnsurePublicOrAuthenticatedPlug.call(conn, []) + end + end end diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index 08bf1c752..7047b8254 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -23,6 +23,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do action_fallback(:errors) + plug( + Pleroma.Plugs.OAuthScopesPlug, + %{scopes: ["read:accounts"]} when action in [:followers, :following] + ) + plug(Pleroma.Web.FederatingPlug when action in [:inbox, :relay]) plug(:set_requester_reachable when action in [:inbox]) plug(:relay_active? when action in [:relay]) diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index 544b9d7d8..0a508d40e 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -6,6 +6,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do use Pleroma.Web, :controller alias Pleroma.Activity alias Pleroma.ModerationLog + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User alias Pleroma.UserInviteToken alias Pleroma.Web.ActivityPub.ActivityPub @@ -23,6 +24,56 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do require Logger + plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action == :list_user_statuses) + + plug( + OAuthScopesPlug, + %{scopes: ["write:statuses"]} when action in [:status_update, :status_delete] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["read"]} + when action in [ + :list_reports, + :report_show, + :right_get, + :get_invite_token, + :invites, + :get_password_reset, + :list_users, + :user_show, + :config_show, + :migrate_to_db, + :migrate_from_db, + :list_log + ] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["write"]} + when action in [ + :report_update_state, + :report_respond, + :user_follow, + :user_unfollow, + :user_delete, + :users_create, + :user_toggle_activation, + :tag_users, + :untag_users, + :right_add, + :right_delete, + :set_activation_status, + :relay_follow, + :relay_unfollow, + :revoke_invite, + :email_invite, + :config_update + ] + ) + @users_page_size 50 action_fallback(:errors) diff --git a/lib/pleroma/web/mastodon_api/controllers/list_controller.ex b/lib/pleroma/web/mastodon_api/controllers/list_controller.ex index 2873deda8..be7089630 100644 --- a/lib/pleroma/web/mastodon_api/controllers/list_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/list_controller.ex @@ -5,11 +5,20 @@ defmodule Pleroma.Web.MastodonAPI.ListController do use Pleroma.Web, :controller + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User alias Pleroma.Web.MastodonAPI.AccountView plug(:list_by_id_and_user when action not in [:index, :create]) + plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action in [:index, :show, :list_accounts]) + + plug( + OAuthScopesPlug, + %{scopes: ["write:lists"]} + when action in [:create, :update, :delete, :add_to_list, :remove_from_list] + ) + action_fallback(Pleroma.Web.MastodonAPI.FallbackController) # GET /api/v1/lists diff --git a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex index 118446c85..704664f5f 100644 --- a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex @@ -53,6 +53,123 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do require Logger require Pleroma.Constants + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action != :index) + + @unauthenticated_access %{fallback: :proceed_unauthenticated, scopes: []} + + plug( + OAuthScopesPlug, + %{scopes: ["read"], skip_instance_privacy_check: true} when action == :index + ) + + plug( + OAuthScopesPlug, + %{scopes: ["read"]} when action in [:suggestions, :verify_app_credentials] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["write:accounts"]} + # Note: the following actions are not permission-secured in Mastodon: + when action in [ + :put_settings, + :update_avatar, + :update_banner, + :update_background, + :set_mascot + ] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["write:accounts"]} + when action in [:pin_status, :unpin_status, :update_credentials] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["read:statuses"]} + when action in [ + :conversations, + :scheduled_statuses, + :show_scheduled_status, + :home_timeline, + :dm_timeline + ] + ) + + plug( + OAuthScopesPlug, + %{@unauthenticated_access | scopes: ["read:statuses"]} + when action in [:user_statuses, :get_status, :get_context, :status_card, :get_poll] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["write:statuses"]} + when action in [ + :update_scheduled_status, + :delete_scheduled_status, + :post_status, + :delete_status, + :reblog_status, + :unreblog_status, + :poll_vote + ] + ) + + plug(OAuthScopesPlug, %{scopes: ["write:conversations"]} when action == :conversation_read) + + plug( + OAuthScopesPlug, + %{scopes: ["read:accounts"]} + when action in [:endorsements, :verify_credentials, :followers, :following, :get_mascot] + ) + + plug( + OAuthScopesPlug, + %{@unauthenticated_access | scopes: ["read:accounts"]} + when action in [:user, :favourited_by, :reblogged_by] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["read:favourites"]} when action in [:favourites, :user_favourites] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["write:favourites"]} when action in [:fav_status, :unfav_status] + ) + + plug(OAuthScopesPlug, %{scopes: ["read:filters"]} when action in [:get_filters, :get_filter]) + + plug( + OAuthScopesPlug, + %{scopes: ["write:filters"]} when action in [:create_filter, :update_filter, :delete_filter] + ) + + plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action in [:account_lists, :list_timeline]) + + plug(OAuthScopesPlug, %{scopes: ["write:media"]} when action in [:upload, :update_media]) + + plug( + OAuthScopesPlug, + %{scopes: ["read:notifications"]} when action in [:notifications, :get_notification] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["write:notifications"]} + when action in [:clear_notifications, :dismiss_notification, :destroy_multiple_notifications] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["write:reports"]} + when action in [:create_report, :report_update_state, :report_respond] + ) + plug( OAuthScopesPlug, %{scopes: ["follow", "read:blocks"]} when action in [:blocks, :domain_blocks] @@ -64,6 +181,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do when action in [:block, :unblock, :block_domain, :unblock_domain] ) + plug(OAuthScopesPlug, %{scopes: ["read:follows"]} when action == :relationships) plug(OAuthScopesPlug, %{scopes: ["follow", "read:follows"]} when action == :follow_requests) plug( @@ -84,8 +202,15 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do plug( OAuthScopesPlug, - %{scopes: ["write:mutes"]} - when action in [:mute_conversation, :unmute_conversation] + %{scopes: ["write:mutes"]} when action in [:mute_conversation, :unmute_conversation] + ) + + # Note: scopes not present in Mastodon: read:bookmarks, write:bookmarks + plug(OAuthScopesPlug, %{scopes: ["read:bookmarks"]} when action == :bookmarks) + + plug( + OAuthScopesPlug, + %{scopes: ["write:bookmarks"]} when action in [:bookmark_status, :unbookmark_status] ) @rate_limited_relations_actions ~w(follow unfollow)a @@ -776,7 +901,7 @@ def dismiss_notification(%{assigns: %{user: user}} = conn, %{"id" => id} = _para end end - def destroy_multiple(%{assigns: %{user: user}} = conn, %{"ids" => ids} = _params) do + def destroy_multiple_notifications(%{assigns: %{user: user}} = conn, %{"ids" => ids} = _params) do Notification.destroy_multiple(user, ids) json(conn, %{}) end @@ -1488,6 +1613,8 @@ def empty_object(conn, _) do json(conn, %{}) end + def endorsements(conn, params), do: empty_array(conn, params) + def get_filters(%{assigns: %{user: user}} = conn, _) do filters = Filter.get_filters(user) res = FilterView.render("filters.json", filters: filters) @@ -1610,7 +1737,7 @@ def status_card(%{assigns: %{user: user}} = conn, %{"id" => status_id}) do end end - def reports(%{assigns: %{user: user}} = conn, params) do + def create_report(%{assigns: %{user: user}} = conn, params) do case CommonAPI.report(user, params) do {:ok, activity} -> conn diff --git a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex index 9072aa7a4..f49ca89ed 100644 --- a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex @@ -6,6 +6,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do use Pleroma.Web, :controller alias Pleroma.Activity + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Plugs.RateLimiter alias Pleroma.Repo alias Pleroma.User @@ -15,6 +16,10 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do alias Pleroma.Web.MastodonAPI.StatusView require Logger + + # Note: Mastodon doesn't allow unauthenticated access (requires read:accounts / read:search) + plug(OAuthScopesPlug, %{scopes: ["read:search"], fallback: :proceed_unauthenticated}) + plug(RateLimiter, :search when action in [:search, :search2, :account_search]) def account_search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do diff --git a/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex b/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex index e2b17aab1..287eebf92 100644 --- a/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex @@ -12,6 +12,8 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionController do action_fallback(:errors) + plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["push"]}) + # Creates PushSubscription # POST /api/v1/push/subscription # diff --git a/lib/pleroma/web/pleroma_api/pleroma_api_controller.ex b/lib/pleroma/web/pleroma_api/pleroma_api_controller.ex index f4df3b024..17c568a9d 100644 --- a/lib/pleroma/web/pleroma_api/pleroma_api_controller.ex +++ b/lib/pleroma/web/pleroma_api/pleroma_api_controller.ex @@ -9,11 +9,24 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do alias Pleroma.Conversation.Participation alias Pleroma.Notification + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.MastodonAPI.ConversationView alias Pleroma.Web.MastodonAPI.NotificationView alias Pleroma.Web.MastodonAPI.StatusView + plug( + OAuthScopesPlug, + %{scopes: ["read:statuses"]} when action in [:conversation, :conversation_statuses] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["write:conversations"]} when action in [:conversations, :conversation_read] + ) + + plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action == :read_notification) + def conversation(%{assigns: %{user: user}} = conn, %{"id" => participation_id}) do with %Participation{} = participation <- Participation.get(participation_id), true <- user.id == participation.user_id do diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 8c93e535e..593da01fd 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -87,27 +87,6 @@ defmodule Pleroma.Web.Router do plug(Pleroma.Plugs.EnsureUserKeyPlug) end - pipeline :oauth_read_or_public do - plug(Pleroma.Plugs.OAuthScopesPlug, %{ - scopes: ["read"], - fallback: :proceed_unauthenticated - }) - - plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) - end - - pipeline :oauth_read do - plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["read"]}) - end - - pipeline :oauth_write do - plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["write"]}) - end - - pipeline :oauth_push do - plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["push"]}) - end - pipeline :well_known do plug(:accepts, ["json", "jrd+json", "xml", "xrd+xml"]) end @@ -149,7 +128,12 @@ defmodule Pleroma.Web.Router do end scope "/api/pleroma/admin", Pleroma.Web.AdminAPI do - pipe_through([:admin_api, :oauth_write]) + pipe_through(:admin_api) + + get("/reports", AdminAPIController, :list_reports) + get("/reports/:id", AdminAPIController, :report_show) + put("/reports/:id", AdminAPIController, :report_update_state) + post("/reports/:id/respond", AdminAPIController, :report_respond) post("/users/follow", AdminAPIController, :user_follow) post("/users/unfollow", AdminAPIController, :user_unfollow) @@ -184,15 +168,6 @@ defmodule Pleroma.Web.Router do get("/users", AdminAPIController, :list_users) get("/users/:nickname", AdminAPIController, :user_show) - get("/users/:nickname/statuses", AdminAPIController, :list_user_statuses) - - get("/reports", AdminAPIController, :list_reports) - get("/reports/:id", AdminAPIController, :report_show) - put("/reports/:id", AdminAPIController, :report_update_state) - post("/reports/:id/respond", AdminAPIController, :report_respond) - - put("/statuses/:id", AdminAPIController, :status_update) - delete("/statuses/:id", AdminAPIController, :status_delete) get("/config", AdminAPIController, :config_show) post("/config", AdminAPIController, :config_update) @@ -200,6 +175,10 @@ defmodule Pleroma.Web.Router do get("/config/migrate_from_db", AdminAPIController, :migrate_from_db) get("/moderation_log", AdminAPIController, :list_log) + + get("/users/:nickname/statuses", AdminAPIController, :list_user_statuses) + put("/statuses/:id", AdminAPIController, :status_update) + delete("/statuses/:id", AdminAPIController, :status_delete) end scope "/", Pleroma.Web.TwitterAPI do @@ -213,19 +192,13 @@ defmodule Pleroma.Web.Router do scope "/api/pleroma", Pleroma.Web.TwitterAPI do pipe_through(:authenticated_api) - scope [] do - pipe_through(:oauth_write) + post("/change_password", UtilController, :change_password) + post("/delete_account", UtilController, :delete_account) + put("/notification_settings", UtilController, :update_notificaton_settings) + post("/disable_account", UtilController, :disable_account) - post("/change_password", UtilController, :change_password) - post("/delete_account", UtilController, :delete_account) - put("/notification_settings", UtilController, :update_notificaton_settings) - post("/disable_account", UtilController, :disable_account) - end - - scope [] do - post("/blocks_import", UtilController, :blocks_import) - post("/follow_import", UtilController, :follow_import) - end + post("/blocks_import", UtilController, :blocks_import) + post("/follow_import", UtilController, :follow_import) end scope "/oauth", Pleroma.Web.OAuth do @@ -252,148 +225,134 @@ defmodule Pleroma.Web.Router do scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do pipe_through(:authenticated_api) - scope [] do - pipe_through(:oauth_read) - get("/conversations/:id/statuses", PleromaAPIController, :conversation_statuses) - get("/conversations/:id", PleromaAPIController, :conversation) - end - - scope [] do - pipe_through(:oauth_write) - patch("/conversations/:id", PleromaAPIController, :update_conversation) - post("/notifications/read", PleromaAPIController, :read_notification) - end + get("/conversations/:id/statuses", PleromaAPIController, :conversation_statuses) + get("/conversations/:id", PleromaAPIController, :conversation) + patch("/conversations/:id", PleromaAPIController, :update_conversation) + post("/notifications/read", PleromaAPIController, :read_notification) end scope "/api/v1", Pleroma.Web.MastodonAPI do pipe_through(:authenticated_api) - scope [] do - pipe_through(:oauth_read) + get("/blocks", MastodonAPIController, :blocks) + get("/mutes", MastodonAPIController, :mutes) + get("/domain_blocks", MastodonAPIController, :domain_blocks) - get("/accounts/verify_credentials", MastodonAPIController, :verify_credentials) + get("/accounts/:id/lists", MastodonAPIController, :account_lists) + get("/lists", ListController, :index) + get("/lists/:id", ListController, :show) + get("/lists/:id/accounts", ListController, :list_accounts) - get("/accounts/relationships", MastodonAPIController, :relationships) + post("/notifications/clear", MastodonAPIController, :clear_notifications) + post("/notifications/dismiss", MastodonAPIController, :dismiss_notification) + get("/notifications", MastodonAPIController, :notifications) + get("/notifications/:id", MastodonAPIController, :get_notification) - get("/accounts/:id/lists", MastodonAPIController, :account_lists) - get("/accounts/:id/identity_proofs", MastodonAPIController, :empty_array) + delete( + "/notifications/destroy_multiple", + MastodonAPIController, + :destroy_multiple_notifications + ) - get("/follow_requests", MastodonAPIController, :follow_requests) - get("/blocks", MastodonAPIController, :blocks) - get("/mutes", MastodonAPIController, :mutes) + # Note: not present in Mastodon + get("/bookmarks", MastodonAPIController, :bookmarks) - get("/timelines/home", MastodonAPIController, :home_timeline) - get("/timelines/direct", MastodonAPIController, :dm_timeline) + get("/accounts/:id/identity_proofs", MastodonAPIController, :empty_array) - get("/favourites", MastodonAPIController, :favourites) - get("/bookmarks", MastodonAPIController, :bookmarks) + get("/favourites", MastodonAPIController, :favourites) - post("/notifications/clear", MastodonAPIController, :clear_notifications) - post("/notifications/dismiss", MastodonAPIController, :dismiss_notification) - get("/notifications", MastodonAPIController, :notifications) - get("/notifications/:id", MastodonAPIController, :get_notification) - delete("/notifications/destroy_multiple", MastodonAPIController, :destroy_multiple) + get("/accounts/relationships", MastodonAPIController, :relationships) - get("/scheduled_statuses", MastodonAPIController, :scheduled_statuses) - get("/scheduled_statuses/:id", MastodonAPIController, :show_scheduled_status) + get("/accounts/verify_credentials", MastodonAPIController, :verify_credentials) - get("/lists", ListController, :index) - get("/lists/:id", ListController, :show) - get("/lists/:id/accounts", ListController, :list_accounts) + get("/timelines/home", MastodonAPIController, :home_timeline) + get("/timelines/direct", MastodonAPIController, :dm_timeline) - get("/domain_blocks", MastodonAPIController, :domain_blocks) + get("/suggestions", MastodonAPIController, :suggestions) + get("/scheduled_statuses", MastodonAPIController, :scheduled_statuses) + get("/scheduled_statuses/:id", MastodonAPIController, :show_scheduled_status) + get("/follow_requests", MastodonAPIController, :follow_requests) + get("/filters", MastodonAPIController, :get_filters) + get("/endorsements", MastodonAPIController, :endorsements) + get("/conversations", MastodonAPIController, :conversations) + post("/conversations/:id/read", MastodonAPIController, :conversation_read) - get("/filters", MastodonAPIController, :get_filters) + delete("/lists/:id", ListController, :delete) + post("/lists", ListController, :create) + put("/lists/:id", ListController, :update) - get("/suggestions", MastodonAPIController, :suggestions) + post("/lists/:id/accounts", ListController, :add_to_list) + delete("/lists/:id/accounts", ListController, :remove_from_list) - get("/conversations", MastodonAPIController, :conversations) - post("/conversations/:id/read", MastodonAPIController, :conversation_read) + post("/reports", MastodonAPIController, :create_report) - get("/endorsements", MastodonAPIController, :empty_array) - end + patch("/pleroma/accounts/update_avatar", MastodonAPIController, :update_avatar) + patch("/pleroma/accounts/update_banner", MastodonAPIController, :update_banner) + patch("/pleroma/accounts/update_background", MastodonAPIController, :update_background) - scope [] do - pipe_through(:oauth_write) + get("/pleroma/mascot", MastodonAPIController, :get_mascot) + put("/pleroma/mascot", MastodonAPIController, :set_mascot) - patch("/accounts/update_credentials", MastodonAPIController, :update_credentials) + post("/media", MastodonAPIController, :upload) + put("/media/:id", MastodonAPIController, :update_media) - post("/statuses", MastodonAPIController, :post_status) - delete("/statuses/:id", MastodonAPIController, :delete_status) + patch("/accounts/update_credentials", MastodonAPIController, :update_credentials) - post("/statuses/:id/reblog", MastodonAPIController, :reblog_status) - post("/statuses/:id/unreblog", MastodonAPIController, :unreblog_status) - post("/statuses/:id/favourite", MastodonAPIController, :fav_status) - post("/statuses/:id/unfavourite", MastodonAPIController, :unfav_status) - post("/statuses/:id/pin", MastodonAPIController, :pin_status) - post("/statuses/:id/unpin", MastodonAPIController, :unpin_status) - post("/statuses/:id/bookmark", MastodonAPIController, :bookmark_status) - post("/statuses/:id/unbookmark", MastodonAPIController, :unbookmark_status) - post("/statuses/:id/mute", MastodonAPIController, :mute_conversation) - post("/statuses/:id/unmute", MastodonAPIController, :unmute_conversation) + post("/polls/:id/votes", MastodonAPIController, :poll_vote) - put("/scheduled_statuses/:id", MastodonAPIController, :update_scheduled_status) - delete("/scheduled_statuses/:id", MastodonAPIController, :delete_scheduled_status) + post("/statuses/:id/reblog", MastodonAPIController, :reblog_status) + post("/statuses/:id/unreblog", MastodonAPIController, :unreblog_status) - post("/polls/:id/votes", MastodonAPIController, :poll_vote) + post("/statuses/:id/pin", MastodonAPIController, :pin_status) + post("/statuses/:id/unpin", MastodonAPIController, :unpin_status) - post("/media", MastodonAPIController, :upload) - put("/media/:id", MastodonAPIController, :update_media) + post("/statuses/:id/mute", MastodonAPIController, :mute_conversation) + post("/statuses/:id/unmute", MastodonAPIController, :unmute_conversation) - delete("/lists/:id", ListController, :delete) - post("/lists", ListController, :create) - put("/lists/:id", ListController, :update) + post("/statuses/:id/favourite", MastodonAPIController, :fav_status) + post("/statuses/:id/unfavourite", MastodonAPIController, :unfav_status) - post("/lists/:id/accounts", ListController, :add_to_list) - delete("/lists/:id/accounts", ListController, :remove_from_list) + post("/statuses", MastodonAPIController, :post_status) + delete("/statuses/:id", MastodonAPIController, :delete_status) - post("/filters", MastodonAPIController, :create_filter) - get("/filters/:id", MastodonAPIController, :get_filter) - put("/filters/:id", MastodonAPIController, :update_filter) - delete("/filters/:id", MastodonAPIController, :delete_filter) + put("/scheduled_statuses/:id", MastodonAPIController, :update_scheduled_status) + delete("/scheduled_statuses/:id", MastodonAPIController, :delete_scheduled_status) - patch("/pleroma/accounts/update_avatar", MastodonAPIController, :update_avatar) - patch("/pleroma/accounts/update_banner", MastodonAPIController, :update_banner) - patch("/pleroma/accounts/update_background", MastodonAPIController, :update_background) + post("/filters", MastodonAPIController, :create_filter) + get("/filters/:id", MastodonAPIController, :get_filter) + put("/filters/:id", MastodonAPIController, :update_filter) + delete("/filters/:id", MastodonAPIController, :delete_filter) - get("/pleroma/mascot", MastodonAPIController, :get_mascot) - put("/pleroma/mascot", MastodonAPIController, :set_mascot) + post("/follows", MastodonAPIController, :follow) + post("/accounts/:id/follow", MastodonAPIController, :follow) - post("/reports", MastodonAPIController, :reports) - end + post("/accounts/:id/unfollow", MastodonAPIController, :unfollow) + post("/accounts/:id/block", MastodonAPIController, :block) + post("/accounts/:id/unblock", MastodonAPIController, :unblock) + post("/accounts/:id/mute", MastodonAPIController, :mute) + post("/accounts/:id/unmute", MastodonAPIController, :unmute) - scope [] do - post("/follows", MastodonAPIController, :follow) - post("/accounts/:id/follow", MastodonAPIController, :follow) + post("/follow_requests/:id/authorize", MastodonAPIController, :authorize_follow_request) + post("/follow_requests/:id/reject", MastodonAPIController, :reject_follow_request) - post("/accounts/:id/unfollow", MastodonAPIController, :unfollow) - post("/accounts/:id/block", MastodonAPIController, :block) - post("/accounts/:id/unblock", MastodonAPIController, :unblock) - post("/accounts/:id/mute", MastodonAPIController, :mute) - post("/accounts/:id/unmute", MastodonAPIController, :unmute) + post("/domain_blocks", MastodonAPIController, :block_domain) + delete("/domain_blocks", MastodonAPIController, :unblock_domain) - post("/follow_requests/:id/authorize", MastodonAPIController, :authorize_follow_request) - post("/follow_requests/:id/reject", MastodonAPIController, :reject_follow_request) + post("/pleroma/accounts/:id/subscribe", MastodonAPIController, :subscribe) + post("/pleroma/accounts/:id/unsubscribe", MastodonAPIController, :unsubscribe) - post("/domain_blocks", MastodonAPIController, :block_domain) - delete("/domain_blocks", MastodonAPIController, :unblock_domain) + post("/push/subscription", SubscriptionController, :create) + get("/push/subscription", SubscriptionController, :get) + put("/push/subscription", SubscriptionController, :update) + delete("/push/subscription", SubscriptionController, :delete) - post("/pleroma/accounts/:id/subscribe", MastodonAPIController, :subscribe) - post("/pleroma/accounts/:id/unsubscribe", MastodonAPIController, :unsubscribe) - end - - scope [] do - pipe_through(:oauth_push) - - post("/push/subscription", SubscriptionController, :create) - get("/push/subscription", SubscriptionController, :get) - put("/push/subscription", SubscriptionController, :update) - delete("/push/subscription", SubscriptionController, :delete) - end + # Note: not present in Mastodon: bookmark, unbookmark + post("/statuses/:id/bookmark", MastodonAPIController, :bookmark_status) + post("/statuses/:id/unbookmark", MastodonAPIController, :unbookmark_status) end scope "/api/web", Pleroma.Web.MastodonAPI do - pipe_through([:authenticated_api, :oauth_write]) + pipe_through(:authenticated_api) put("/settings", MastodonAPIController, :put_settings) end @@ -424,31 +383,29 @@ defmodule Pleroma.Web.Router do :account_confirmation_resend ) - scope [] do - pipe_through(:oauth_read_or_public) + get("/timelines/public", MastodonAPIController, :public_timeline) + get("/timelines/tag/:tag", MastodonAPIController, :hashtag_timeline) - get("/timelines/public", MastodonAPIController, :public_timeline) - get("/timelines/tag/:tag", MastodonAPIController, :hashtag_timeline) - get("/timelines/list/:list_id", MastodonAPIController, :list_timeline) + get("/pleroma/accounts/:id/favourites", MastodonAPIController, :user_favourites) - get("/statuses/:id", MastodonAPIController, :get_status) - get("/statuses/:id/context", MastodonAPIController, :get_context) + get("/search", SearchController, :search) - get("/polls/:id", MastodonAPIController, :get_poll) + get("/polls/:id", MastodonAPIController, :get_poll) - get("/accounts/:id/statuses", MastodonAPIController, :user_statuses) - get("/accounts/:id/followers", MastodonAPIController, :followers) - get("/accounts/:id/following", MastodonAPIController, :following) - get("/accounts/:id", MastodonAPIController, :user) + get("/accounts/:id/followers", MastodonAPIController, :followers) + get("/accounts/:id/following", MastodonAPIController, :following) - get("/search", SearchController, :search) + get("/timelines/list/:list_id", MastodonAPIController, :list_timeline) - get("/pleroma/accounts/:id/favourites", MastodonAPIController, :user_favourites) - end + get("/accounts/:id", MastodonAPIController, :user) + + get("/accounts/:id/statuses", MastodonAPIController, :user_statuses) + get("/statuses/:id", MastodonAPIController, :get_status) + get("/statuses/:id/context", MastodonAPIController, :get_context) end scope "/api/v2", Pleroma.Web.MastodonAPI do - pipe_through([:api, :oauth_read_or_public]) + pipe_through(:api) get("/search", SearchController, :search2) end @@ -478,12 +435,7 @@ defmodule Pleroma.Web.Router do get("/oauth_tokens", TwitterAPI.Controller, :oauth_tokens) delete("/oauth_tokens/:id", TwitterAPI.Controller, :revoke_token) - - scope [] do - pipe_through(:oauth_read) - - post("/qvitter/statuses/notifications/read", TwitterAPI.Controller, :notifications_read) - end + post("/qvitter/statuses/notifications/read", TwitterAPI.Controller, :notifications_read) end pipeline :ap_service_actor do @@ -547,26 +499,16 @@ defmodule Pleroma.Web.Router do scope "/", Pleroma.Web.ActivityPub do pipe_through([:activitypub_client]) - scope [] do - pipe_through(:oauth_read) - get("/api/ap/whoami", ActivityPubController, :whoami) - get("/users/:nickname/inbox", ActivityPubController, :read_inbox) - end - - scope [] do - pipe_through(:oauth_write) - post("/users/:nickname/outbox", ActivityPubController, :update_outbox) - end - - scope [] do - pipe_through(:oauth_read_or_public) - get("/users/:nickname/followers", ActivityPubController, :followers) - get("/users/:nickname/following", ActivityPubController, :following) - end + get("/api/ap/whoami", ActivityPubController, :whoami) + get("/users/:nickname/inbox", ActivityPubController, :read_inbox) + post("/users/:nickname/outbox", ActivityPubController, :update_outbox) + get("/users/:nickname/followers", ActivityPubController, :followers) + get("/users/:nickname/following", ActivityPubController, :following) end scope "/", Pleroma.Web.ActivityPub do pipe_through(:activitypub) + post("/inbox", ActivityPubController, :inbox) post("/users/:nickname/inbox", ActivityPubController, :inbox) end @@ -612,10 +554,7 @@ defmodule Pleroma.Web.Router do post("/auth/password", MastodonAPIController, :password_reset) - scope [] do - pipe_through(:oauth_read) - get("/web/*path", MastodonAPIController, :index) - end + get("/web/*path", MastodonAPIController, :index) end pipeline :remote_media do diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index 1c6ad5057..82ed0c287 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -27,6 +27,17 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do plug(OAuthScopesPlug, %{scopes: ["follow", "write:blocks"]} when action == :blocks_import) + plug( + OAuthScopesPlug, + %{scopes: ["write:accounts"]} + when action in [ + :change_password, + :delete_account, + :update_notificaton_settings, + :disable_account + ] + ) + plug(Pleroma.Plugs.SetFormatPlug when action in [:config, :version]) def help_test(conn, _params) do diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index 42234ae09..42bd74eb5 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -7,12 +7,15 @@ defmodule Pleroma.Web.TwitterAPI.Controller do alias Ecto.Changeset alias Pleroma.Notification + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User alias Pleroma.Web.OAuth.Token alias Pleroma.Web.TwitterAPI.TokenView require Logger + plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action == :notifications_read) + action_fallback(:errors) def confirm_email(conn, %{"user_id" => uid, "token" => token}) do diff --git a/test/support/factory.ex b/test/support/factory.ex index 719115003..c14c8ddb3 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -283,6 +283,7 @@ def oauth_token_factory do %Pleroma.Web.OAuth.Token{ token: :crypto.strong_rand_bytes(32) |> Base.url_encode64(), + scopes: ["read"], refresh_token: :crypto.strong_rand_bytes(32) |> Base.url_encode64(), user: build(:user), app_id: oauth_app.id, diff --git a/test/web/mastodon_api/controllers/mastodon_api_controller/update_credentials_test.exs b/test/web/mastodon_api/controllers/mastodon_api_controller/update_credentials_test.exs index 87ee82050..1680ec122 100644 --- a/test/web/mastodon_api/controllers/mastodon_api_controller/update_credentials_test.exs +++ b/test/web/mastodon_api/controllers/mastodon_api_controller/update_credentials_test.exs @@ -257,7 +257,7 @@ test "updates the user's background", %{conn: conn} do assert user_response["pleroma"]["background_image"] end - test "requires 'write' permission", %{conn: conn} do + test "requires 'write:accounts' permission", %{conn: conn} do token1 = insert(:oauth_token, scopes: ["read"]) token2 = insert(:oauth_token, scopes: ["write", "follow"]) @@ -268,7 +268,8 @@ test "requires 'write' permission", %{conn: conn} do |> patch("/api/v1/accounts/update_credentials", %{}) if token == token1 do - assert %{"error" => "Insufficient permissions: write."} == json_response(conn, 403) + assert %{"error" => "Insufficient permissions: write:accounts."} == + json_response(conn, 403) else assert json_response(conn, 200) end diff --git a/test/web/oauth/oauth_controller_test.exs b/test/web/oauth/oauth_controller_test.exs index b492c7794..e919ea112 100644 --- a/test/web/oauth/oauth_controller_test.exs +++ b/test/web/oauth/oauth_controller_test.exs @@ -556,7 +556,7 @@ test "redirects with oauth authorization" do "password" => "test", "client_id" => app.client_id, "redirect_uri" => redirect_uri, - "scope" => "read write", + "scope" => "read:subscope write", "state" => "statepassed" } }) @@ -569,7 +569,7 @@ test "redirects with oauth authorization" do assert %{"state" => "statepassed", "code" => code} = query auth = Repo.get_by(Authorization, token: code) assert auth - assert auth.scopes == ["read", "write"] + assert auth.scopes == ["read:subscope", "write"] end test "returns 401 for wrong credentials", %{conn: conn} do @@ -626,7 +626,7 @@ test "returns 401 for missing scopes", %{conn: conn} do assert result =~ "This action is outside the authorized scopes" end - test "returns 401 for scopes beyond app scopes", %{conn: conn} do + test "returns 401 for scopes beyond app scopes hierarchy", %{conn: conn} do user = insert(:user) app = insert(:oauth_app, scopes: ["read", "write"]) redirect_uri = OAuthController.default_redirect_uri(app) From e7afb67c5c26fcb07c1f8d4390c0573cebef9520 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Tue, 17 Sep 2019 16:16:11 +0300 Subject: [PATCH 09/37] [#1260] Rate-limiting for create authentication and related requests. --- config/config.exs | 2 +- config/description.exs | 9 ++++++++- lib/pleroma/web/mongooseim/mongoose_im_controller.ex | 5 +++++ lib/pleroma/web/oauth/oauth_controller.ex | 1 + 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/config/config.exs b/config/config.exs index c7e0cf09f..15979702f 100644 --- a/config/config.exs +++ b/config/config.exs @@ -585,7 +585,7 @@ config :http_signatures, adapter: Pleroma.Signature -config :pleroma, :rate_limit, nil +config :pleroma, :rate_limit, authentication: {60_000, 15} config :pleroma, Pleroma.ActivityExpiration, enabled: true diff --git a/config/description.exs b/config/description.exs index 32d36d6d6..bd0378e00 100644 --- a/config/description.exs +++ b/config/description.exs @@ -2424,7 +2424,8 @@ group: :pleroma, key: :rate_limit, type: :group, - description: "Rate limit settings. This is an advanced feature and disabled by default.", + description: + "Rate limit settings. This is an advanced feature enabled only for :authentication by default.", children: [ %{ key: :search, @@ -2463,6 +2464,12 @@ description: "for fav / unfav or reblog / unreblog actions on the same status by the same user", suggestions: [{1000, 10}, [{10_000, 10}, {10_000, 50}]] + }, + %{ + key: :authentication, + type: [:tuple, {:list, :tuple}], + description: "for authentication create / password check / user existence check requests", + suggestions: [{60_000, 15}] } ] }, diff --git a/lib/pleroma/web/mongooseim/mongoose_im_controller.ex b/lib/pleroma/web/mongooseim/mongoose_im_controller.ex index b786a521b..6ed181cff 100644 --- a/lib/pleroma/web/mongooseim/mongoose_im_controller.ex +++ b/lib/pleroma/web/mongooseim/mongoose_im_controller.ex @@ -4,10 +4,15 @@ defmodule Pleroma.Web.MongooseIM.MongooseIMController do use Pleroma.Web, :controller + alias Comeonin.Pbkdf2 + alias Pleroma.Plugs.RateLimiter alias Pleroma.Repo alias Pleroma.User + plug(RateLimiter, :authentication when action in [:user_exists, :check_password]) + plug(RateLimiter, {:authentication, params: ["user"]} when action == :check_password) + def user_exists(conn, %{"user" => username}) do with %User{} <- Repo.get_by(User, nickname: username, local: true) do conn diff --git a/lib/pleroma/web/oauth/oauth_controller.ex b/lib/pleroma/web/oauth/oauth_controller.ex index 81eae2c8b..281c7d2d8 100644 --- a/lib/pleroma/web/oauth/oauth_controller.ex +++ b/lib/pleroma/web/oauth/oauth_controller.ex @@ -24,6 +24,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do plug(:fetch_session) plug(:fetch_flash) + plug(Pleroma.Plugs.RateLimiter, :authentication when action == :create_authorization) action_fallback(Pleroma.Web.OAuth.FallbackController) From 76068873dbf9da191dd2487158ca88df198b811a Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Tue, 17 Sep 2019 22:19:39 +0300 Subject: [PATCH 10/37] [#1234] Defined admin OAuth scopes, refined other scopes. Added tests. --- .../web/admin_api/admin_api_controller.ex | 71 +++--- .../controllers/mastodon_api_controller.ex | 23 +- test/plugs/oauth_scopes_plug_test.exs | 227 ++++++++++++------ 3 files changed, 210 insertions(+), 111 deletions(-) diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index 0a508d40e..fa69a23d9 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -24,38 +24,20 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do require Logger - plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action == :list_user_statuses) - plug( OAuthScopesPlug, - %{scopes: ["write:statuses"]} when action in [:status_update, :status_delete] + %{scopes: ["admin:read:accounts", "read:accounts"]} + when action in [:list_users, :user_show, :right_get, :invites] ) plug( OAuthScopesPlug, - %{scopes: ["read"]} + %{scopes: ["admin:write", "write:accounts"]} when action in [ - :list_reports, - :report_show, - :right_get, :get_invite_token, - :invites, + :revoke_invite, + :email_invite, :get_password_reset, - :list_users, - :user_show, - :config_show, - :migrate_to_db, - :migrate_from_db, - :list_log - ] - ) - - plug( - OAuthScopesPlug, - %{scopes: ["write"]} - when action in [ - :report_update_state, - :report_respond, :user_follow, :user_unfollow, :user_delete, @@ -65,15 +47,44 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do :untag_users, :right_add, :right_delete, - :set_activation_status, - :relay_follow, - :relay_unfollow, - :revoke_invite, - :email_invite, - :config_update + :set_activation_status ] ) + plug( + OAuthScopesPlug, + %{scopes: ["admin:read:reports", "read:reports"]} when action in [:list_reports, :report_show] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["admin:write:reports", "write:reports"]} + when action in [:report_update_state, :report_respond] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["admin:read:statuses", "read:statuses"]} when action == :list_user_statuses + ) + + plug( + OAuthScopesPlug, + %{scopes: ["admin:write:statuses", "write:statuses"]} + when action in [:status_update, :status_delete] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["admin:read", "read"]} + when action in [:config_show, :migrate_to_db, :migrate_from_db, :list_log] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["admin:write", "write"]} + when action in [:relay_follow, :relay_unfollow, :config_update] + ) + @users_page_size 50 action_fallback(:errors) @@ -451,7 +462,7 @@ def email_invite(%{assigns: %{user: user}} = conn, %{"email" => email} = params) end end - @doc "Get a account registeration invite token (base64 string)" + @doc "Get a account registration invite token (base64 string)" def get_invite_token(conn, params) do options = params["invite"] || %{} {:ok, invite} = UserInviteToken.create_invite(options) diff --git a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex index c5632bb5e..d7a83a2f5 100644 --- a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex @@ -53,13 +53,13 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do require Logger require Pleroma.Constants - plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action != :index) - @unauthenticated_access %{fallback: :proceed_unauthenticated, scopes: []} + # Note: :index action handles attempt of unauthenticated access to private instance with redirect plug( OAuthScopesPlug, - %{scopes: ["read"], skip_instance_privacy_check: true} when action == :index + Map.merge(@unauthenticated_access, %{scopes: ["read"], skip_instance_privacy_check: true}) + when action == :index ) plug( @@ -220,6 +220,23 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do %{scopes: ["write:bookmarks"]} when action in [:bookmark_status, :unbookmark_status] ) + # An extra safety measure for possible actions not guarded by OAuth permissions specification + plug( + Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug + when action not in [ + :account_register, + :create_app, + :index, + :login, + :logout, + :password_reset, + :account_confirmation_resend, + :masto_instance, + :peers, + :custom_emojis + ] + ) + @rate_limited_relations_actions ~w(follow unfollow)a @rate_limited_status_actions ~w(reblog_status unreblog_status fav_status unfav_status diff --git a/test/plugs/oauth_scopes_plug_test.exs b/test/plugs/oauth_scopes_plug_test.exs index 9b0a2e702..3b895a6e4 100644 --- a/test/plugs/oauth_scopes_plug_test.exs +++ b/test/plugs/oauth_scopes_plug_test.exs @@ -6,23 +6,47 @@ defmodule Pleroma.Plugs.OAuthScopesPlugTest do use Pleroma.Web.ConnCase, async: true alias Pleroma.Plugs.OAuthScopesPlug + alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug alias Pleroma.Repo + import Mock import Pleroma.Factory - test "proceeds with no op if `assigns[:token]` is nil", %{conn: conn} do - conn = - conn - |> assign(:user, insert(:user)) - |> OAuthScopesPlug.call(%{scopes: ["read"]}) - - refute conn.halted - assert conn.assigns[:user] + setup_with_mocks([{EnsurePublicOrAuthenticatedPlug, [], [call: fn conn, _ -> conn end]}]) do + :ok end - test "proceeds with no op if `token.scopes` fulfill specified 'any of' conditions", %{ - conn: conn - } do + describe "when `assigns[:token]` is nil, " do + test "with :skip_instance_privacy_check option, proceeds with no op", %{conn: conn} do + conn = + conn + |> assign(:user, insert(:user)) + |> OAuthScopesPlug.call(%{scopes: ["read"], skip_instance_privacy_check: true}) + + refute conn.halted + assert conn.assigns[:user] + + refute called(EnsurePublicOrAuthenticatedPlug.call(conn, :_)) + end + + test "without :skip_instance_privacy_check option, calls EnsurePublicOrAuthenticatedPlug", %{ + conn: conn + } do + conn = + conn + |> assign(:user, insert(:user)) + |> OAuthScopesPlug.call(%{scopes: ["read"]}) + + refute conn.halted + assert conn.assigns[:user] + + assert called(EnsurePublicOrAuthenticatedPlug.call(conn, :_)) + end + end + + test "if `token.scopes` fulfills specified 'any of' conditions, " <> + "proceeds with no op", + %{conn: conn} do token = insert(:oauth_token, scopes: ["read", "write"]) |> Repo.preload(:user) conn = @@ -35,9 +59,9 @@ test "proceeds with no op if `token.scopes` fulfill specified 'any of' condition assert conn.assigns[:user] end - test "proceeds with no op if `token.scopes` fulfill specified 'all of' conditions", %{ - conn: conn - } do + test "if `token.scopes` fulfills specified 'all of' conditions, " <> + "proceeds with no op", + %{conn: conn} do token = insert(:oauth_token, scopes: ["scope1", "scope2", "scope3"]) |> Repo.preload(:user) conn = @@ -50,82 +74,112 @@ test "proceeds with no op if `token.scopes` fulfill specified 'all of' condition assert conn.assigns[:user] end - test "proceeds with cleared `assigns[:user]` if `token.scopes` doesn't fulfill specified 'any of' conditions " <> - "and `fallback: :proceed_unauthenticated` option is specified", - %{conn: conn} do - token = insert(:oauth_token, scopes: ["read", "write"]) |> Repo.preload(:user) + describe "with `fallback: :proceed_unauthenticated` option, " do + test "if `token.scopes` doesn't fulfill specified 'any of' conditions, " <> + "clears `assigns[:user]` and calls EnsurePublicOrAuthenticatedPlug", + %{conn: conn} do + token = insert(:oauth_token, scopes: ["read", "write"]) |> Repo.preload(:user) - conn = - conn - |> assign(:user, token.user) - |> assign(:token, token) - |> OAuthScopesPlug.call(%{scopes: ["follow"], fallback: :proceed_unauthenticated}) + conn = + conn + |> assign(:user, token.user) + |> assign(:token, token) + |> OAuthScopesPlug.call(%{scopes: ["follow"], fallback: :proceed_unauthenticated}) - refute conn.halted - refute conn.assigns[:user] + refute conn.halted + refute conn.assigns[:user] + + assert called(EnsurePublicOrAuthenticatedPlug.call(conn, :_)) + end + + test "if `token.scopes` doesn't fulfill specified 'all of' conditions, " <> + "clears `assigns[:user] and calls EnsurePublicOrAuthenticatedPlug", + %{conn: conn} do + token = insert(:oauth_token, scopes: ["read", "write"]) |> Repo.preload(:user) + + conn = + conn + |> assign(:user, token.user) + |> assign(:token, token) + |> OAuthScopesPlug.call(%{ + scopes: ["read", "follow"], + op: :&, + fallback: :proceed_unauthenticated + }) + + refute conn.halted + refute conn.assigns[:user] + + assert called(EnsurePublicOrAuthenticatedPlug.call(conn, :_)) + end + + test "with :skip_instance_privacy_check option, " <> + "if `token.scopes` doesn't fulfill specified conditions, " <> + "clears `assigns[:user]` and does not call EnsurePublicOrAuthenticatedPlug", + %{conn: conn} do + token = insert(:oauth_token, scopes: ["read:statuses", "write"]) |> Repo.preload(:user) + + conn = + conn + |> assign(:user, token.user) + |> assign(:token, token) + |> OAuthScopesPlug.call(%{ + scopes: ["read"], + fallback: :proceed_unauthenticated, + skip_instance_privacy_check: true + }) + + refute conn.halted + refute conn.assigns[:user] + + refute called(EnsurePublicOrAuthenticatedPlug.call(conn, :_)) + end end - test "proceeds with cleared `assigns[:user]` if `token.scopes` doesn't fulfill specified 'all of' conditions " <> - "and `fallback: :proceed_unauthenticated` option is specified", - %{conn: conn} do - token = insert(:oauth_token, scopes: ["read", "write"]) |> Repo.preload(:user) + describe "without :fallback option, " do + test "if `token.scopes` does not fulfill specified 'any of' conditions, " <> + "returns 403 and halts", + %{conn: conn} do + token = insert(:oauth_token, scopes: ["read", "write"]) + any_of_scopes = ["follow"] - conn = - conn - |> assign(:user, token.user) - |> assign(:token, token) - |> OAuthScopesPlug.call(%{ - scopes: ["read", "follow"], - op: :&, - fallback: :proceed_unauthenticated - }) + conn = + conn + |> assign(:token, token) + |> OAuthScopesPlug.call(%{scopes: any_of_scopes}) - refute conn.halted - refute conn.assigns[:user] - end + assert conn.halted + assert 403 == conn.status - test "returns 403 and halts " <> - "in case of no :fallback option and `token.scopes` not fulfilling specified 'any of' conditions", - %{conn: conn} do - token = insert(:oauth_token, scopes: ["read", "write"]) - any_of_scopes = ["follow"] + expected_error = "Insufficient permissions: #{Enum.join(any_of_scopes, ", ")}." + assert Jason.encode!(%{error: expected_error}) == conn.resp_body + end - conn = - conn - |> assign(:token, token) - |> OAuthScopesPlug.call(%{scopes: any_of_scopes}) + test "if `token.scopes` does not fulfill specified 'all of' conditions, " <> + "returns 403 and halts", + %{conn: conn} do + token = insert(:oauth_token, scopes: ["read", "write"]) + all_of_scopes = ["write", "follow"] - assert conn.halted - assert 403 == conn.status + conn = + conn + |> assign(:token, token) + |> OAuthScopesPlug.call(%{scopes: all_of_scopes, op: :&}) - expected_error = "Insufficient permissions: #{Enum.join(any_of_scopes, ", ")}." - assert Jason.encode!(%{error: expected_error}) == conn.resp_body - end + assert conn.halted + assert 403 == conn.status - test "returns 403 and halts " <> - "in case of no :fallback option and `token.scopes` not fulfilling specified 'all of' conditions", - %{conn: conn} do - token = insert(:oauth_token, scopes: ["read", "write"]) - all_of_scopes = ["write", "follow"] + expected_error = + "Insufficient permissions: #{Enum.join(all_of_scopes -- token.scopes, ", ")}." - conn = - conn - |> assign(:token, token) - |> OAuthScopesPlug.call(%{scopes: all_of_scopes, op: :&}) - - assert conn.halted - assert 403 == conn.status - - expected_error = - "Insufficient permissions: #{Enum.join(all_of_scopes -- token.scopes, ", ")}." - - assert Jason.encode!(%{error: expected_error}) == conn.resp_body + assert Jason.encode!(%{error: expected_error}) == conn.resp_body + end end describe "with hierarchical scopes, " do - test "proceeds with no op if `token.scopes` fulfill specified 'any of' conditions", %{ - conn: conn - } do + test "if `token.scopes` fulfills specified 'any of' conditions, " <> + "proceeds with no op", + %{conn: conn} do token = insert(:oauth_token, scopes: ["read", "write"]) |> Repo.preload(:user) conn = @@ -138,9 +192,9 @@ test "proceeds with no op if `token.scopes` fulfill specified 'any of' condition assert conn.assigns[:user] end - test "proceeds with no op if `token.scopes` fulfill specified 'all of' conditions", %{ - conn: conn - } do + test "if `token.scopes` fulfills specified 'all of' conditions, " <> + "proceeds with no op", + %{conn: conn} do token = insert(:oauth_token, scopes: ["scope1", "scope2", "scope3"]) |> Repo.preload(:user) conn = @@ -153,4 +207,21 @@ test "proceeds with no op if `token.scopes` fulfill specified 'all of' condition assert conn.assigns[:user] end end + + describe "filter_descendants/2" do + test "filters scopes which directly match or are ancestors of supported scopes" do + f = fn scopes, supported_scopes -> + OAuthScopesPlug.filter_descendants(scopes, supported_scopes) + end + + assert f.(["read", "follow"], ["write", "read"]) == ["read"] + + assert f.(["read", "write:something", "follow"], ["write", "read"]) == + ["read", "write:something"] + + assert f.(["admin:read"], ["write", "read"]) == [] + + assert f.(["admin:read"], ["write", "admin"]) == ["admin:read"] + end + end end From b4d775161cdbd1e72ed9bdf3363090c30e8c25fc Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Tue, 17 Sep 2019 23:24:55 +0300 Subject: [PATCH 11/37] [#1234] Undone reordering-related changes in router.ex. --- lib/pleroma/web/router.ex | 186 +++++++++++++++++++------------------- 1 file changed, 95 insertions(+), 91 deletions(-) diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 6e80efec4..8448a00a1 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -131,11 +131,6 @@ defmodule Pleroma.Web.Router do scope "/api/pleroma/admin", Pleroma.Web.AdminAPI do pipe_through(:admin_api) - get("/reports", AdminAPIController, :list_reports) - get("/reports/:id", AdminAPIController, :report_show) - put("/reports/:id", AdminAPIController, :report_update_state) - post("/reports/:id/respond", AdminAPIController, :report_respond) - post("/users/follow", AdminAPIController, :user_follow) post("/users/unfollow", AdminAPIController, :user_unfollow) @@ -169,6 +164,15 @@ defmodule Pleroma.Web.Router do get("/users", AdminAPIController, :list_users) get("/users/:nickname", AdminAPIController, :user_show) + get("/users/:nickname/statuses", AdminAPIController, :list_user_statuses) + + get("/reports", AdminAPIController, :list_reports) + get("/reports/:id", AdminAPIController, :report_show) + put("/reports/:id", AdminAPIController, :report_update_state) + post("/reports/:id/respond", AdminAPIController, :report_respond) + + put("/statuses/:id", AdminAPIController, :status_update) + delete("/statuses/:id", AdminAPIController, :status_delete) get("/config", AdminAPIController, :config_show) post("/config", AdminAPIController, :config_update) @@ -176,10 +180,6 @@ defmodule Pleroma.Web.Router do get("/config/migrate_from_db", AdminAPIController, :migrate_from_db) get("/moderation_log", AdminAPIController, :list_log) - - get("/users/:nickname/statuses", AdminAPIController, :list_user_statuses) - put("/statuses/:id", AdminAPIController, :status_update) - delete("/statuses/:id", AdminAPIController, :status_delete) end scope "/", Pleroma.Web.TwitterAPI do @@ -229,6 +229,7 @@ defmodule Pleroma.Web.Router do get("/conversations/:id/statuses", PleromaAPIController, :conversation_statuses) get("/conversations/:id", PleromaAPIController, :conversation) + patch("/conversations/:id", PleromaAPIController, :update_conversation) post("/notifications/read", PleromaAPIController, :read_notification) end @@ -236,14 +237,23 @@ defmodule Pleroma.Web.Router do scope "/api/v1", Pleroma.Web.MastodonAPI do pipe_through(:authenticated_api) - get("/blocks", MastodonAPIController, :blocks) - get("/mutes", MastodonAPIController, :mutes) - get("/domain_blocks", MastodonAPIController, :domain_blocks) + get("/accounts/verify_credentials", MastodonAPIController, :verify_credentials) + + get("/accounts/relationships", MastodonAPIController, :relationships) get("/accounts/:id/lists", MastodonAPIController, :account_lists) - get("/lists", ListController, :index) - get("/lists/:id", ListController, :show) - get("/lists/:id/accounts", ListController, :list_accounts) + get("/accounts/:id/identity_proofs", MastodonAPIController, :empty_array) + + get("/follow_requests", MastodonAPIController, :follow_requests) + get("/blocks", MastodonAPIController, :blocks) + get("/mutes", MastodonAPIController, :mutes) + + get("/timelines/home", MastodonAPIController, :home_timeline) + get("/timelines/direct", MastodonAPIController, :dm_timeline) + + get("/favourites", MastodonAPIController, :favourites) + # Note: not present in Mastodon: bookmarks + get("/bookmarks", MastodonAPIController, :bookmarks) post("/notifications/clear", MastodonAPIController, :clear_notifications) post("/notifications/dismiss", MastodonAPIController, :dismiss_notification) @@ -256,29 +266,50 @@ defmodule Pleroma.Web.Router do :destroy_multiple_notifications ) - # Note: not present in Mastodon - get("/bookmarks", MastodonAPIController, :bookmarks) - - get("/accounts/:id/identity_proofs", MastodonAPIController, :empty_array) - - get("/favourites", MastodonAPIController, :favourites) - - get("/accounts/relationships", MastodonAPIController, :relationships) - - get("/accounts/verify_credentials", MastodonAPIController, :verify_credentials) - - get("/timelines/home", MastodonAPIController, :home_timeline) - get("/timelines/direct", MastodonAPIController, :dm_timeline) - - get("/suggestions", MastodonAPIController, :suggestions) get("/scheduled_statuses", MastodonAPIController, :scheduled_statuses) get("/scheduled_statuses/:id", MastodonAPIController, :show_scheduled_status) - get("/follow_requests", MastodonAPIController, :follow_requests) + + get("/lists", ListController, :index) + get("/lists/:id", ListController, :show) + get("/lists/:id/accounts", ListController, :list_accounts) + + get("/domain_blocks", MastodonAPIController, :domain_blocks) + get("/filters", MastodonAPIController, :get_filters) - get("/endorsements", MastodonAPIController, :endorsements) + + get("/suggestions", MastodonAPIController, :suggestions) + get("/conversations", MastodonAPIController, :conversations) post("/conversations/:id/read", MastodonAPIController, :conversation_read) + get("/endorsements", MastodonAPIController, :endorsements) + + patch("/accounts/update_credentials", MastodonAPIController, :update_credentials) + + post("/statuses", MastodonAPIController, :post_status) + delete("/statuses/:id", MastodonAPIController, :delete_status) + + post("/statuses/:id/reblog", MastodonAPIController, :reblog_status) + post("/statuses/:id/unreblog", MastodonAPIController, :unreblog_status) + post("/statuses/:id/favourite", MastodonAPIController, :fav_status) + post("/statuses/:id/unfavourite", MastodonAPIController, :unfav_status) + post("/statuses/:id/pin", MastodonAPIController, :pin_status) + post("/statuses/:id/unpin", MastodonAPIController, :unpin_status) + # Note: not present in Mastodon: bookmark + post("/statuses/:id/bookmark", MastodonAPIController, :bookmark_status) + # Note: not present in Mastodon: unbookmark + post("/statuses/:id/unbookmark", MastodonAPIController, :unbookmark_status) + post("/statuses/:id/mute", MastodonAPIController, :mute_conversation) + post("/statuses/:id/unmute", MastodonAPIController, :unmute_conversation) + + put("/scheduled_statuses/:id", MastodonAPIController, :update_scheduled_status) + delete("/scheduled_statuses/:id", MastodonAPIController, :delete_scheduled_status) + + post("/polls/:id/votes", MastodonAPIController, :poll_vote) + + post("/media", MastodonAPIController, :upload) + put("/media/:id", MastodonAPIController, :update_media) + delete("/lists/:id", ListController, :delete) post("/lists", ListController, :create) put("/lists/:id", ListController, :update) @@ -286,7 +317,10 @@ defmodule Pleroma.Web.Router do post("/lists/:id/accounts", ListController, :add_to_list) delete("/lists/:id/accounts", ListController, :remove_from_list) - post("/reports", MastodonAPIController, :create_report) + post("/filters", MastodonAPIController, :create_filter) + get("/filters/:id", MastodonAPIController, :get_filter) + put("/filters/:id", MastodonAPIController, :update_filter) + delete("/filters/:id", MastodonAPIController, :delete_filter) patch("/pleroma/accounts/update_avatar", MastodonAPIController, :update_avatar) patch("/pleroma/accounts/update_banner", MastodonAPIController, :update_banner) @@ -295,35 +329,7 @@ defmodule Pleroma.Web.Router do get("/pleroma/mascot", MastodonAPIController, :get_mascot) put("/pleroma/mascot", MastodonAPIController, :set_mascot) - post("/media", MastodonAPIController, :upload) - put("/media/:id", MastodonAPIController, :update_media) - - patch("/accounts/update_credentials", MastodonAPIController, :update_credentials) - - post("/polls/:id/votes", MastodonAPIController, :poll_vote) - - post("/statuses/:id/reblog", MastodonAPIController, :reblog_status) - post("/statuses/:id/unreblog", MastodonAPIController, :unreblog_status) - - post("/statuses/:id/pin", MastodonAPIController, :pin_status) - post("/statuses/:id/unpin", MastodonAPIController, :unpin_status) - - post("/statuses/:id/mute", MastodonAPIController, :mute_conversation) - post("/statuses/:id/unmute", MastodonAPIController, :unmute_conversation) - - post("/statuses/:id/favourite", MastodonAPIController, :fav_status) - post("/statuses/:id/unfavourite", MastodonAPIController, :unfav_status) - - post("/statuses", MastodonAPIController, :post_status) - delete("/statuses/:id", MastodonAPIController, :delete_status) - - put("/scheduled_statuses/:id", MastodonAPIController, :update_scheduled_status) - delete("/scheduled_statuses/:id", MastodonAPIController, :delete_scheduled_status) - - post("/filters", MastodonAPIController, :create_filter) - get("/filters/:id", MastodonAPIController, :get_filter) - put("/filters/:id", MastodonAPIController, :update_filter) - delete("/filters/:id", MastodonAPIController, :delete_filter) + post("/reports", MastodonAPIController, :create_report) post("/follows", MastodonAPIController, :follow) post("/accounts/:id/follow", MastodonAPIController, :follow) @@ -347,10 +353,6 @@ defmodule Pleroma.Web.Router do get("/push/subscription", SubscriptionController, :get) put("/push/subscription", SubscriptionController, :update) delete("/push/subscription", SubscriptionController, :delete) - - # Note: not present in Mastodon: bookmark, unbookmark - post("/statuses/:id/bookmark", MastodonAPIController, :bookmark_status) - post("/statuses/:id/unbookmark", MastodonAPIController, :unbookmark_status) end scope "/api/web", Pleroma.Web.MastodonAPI do @@ -362,46 +364,47 @@ defmodule Pleroma.Web.Router do scope "/api/v1", Pleroma.Web.MastodonAPI do pipe_through(:api) + post("/accounts", MastodonAPIController, :account_register) + get("/instance", MastodonAPIController, :masto_instance) get("/instance/peers", MastodonAPIController, :peers) - post("/apps", MastodonAPIController, :create_app) get("/apps/verify_credentials", MastodonAPIController, :verify_app_credentials) - get("/custom_emojis", MastodonAPIController, :custom_emojis) + get("/statuses/:id/card", MastodonAPIController, :status_card) + + get("/statuses/:id/favourited_by", MastodonAPIController, :favourited_by) + get("/statuses/:id/reblogged_by", MastodonAPIController, :reblogged_by) + get("/trends", MastodonAPIController, :empty_array) get("/accounts/search", SearchController, :account_search) - get("/timelines/public", MastodonAPIController, :public_timeline) - get("/timelines/tag/:tag", MastodonAPIController, :hashtag_timeline) - get("/timelines/list/:list_id", MastodonAPIController, :list_timeline) - - get("/polls/:id", MastodonAPIController, :get_poll) - - post("/accounts", MastodonAPIController, :account_register) - get("/accounts/:id", MastodonAPIController, :user) - get("/accounts/:id/followers", MastodonAPIController, :followers) - get("/accounts/:id/following", MastodonAPIController, :following) - get("/accounts/:id/statuses", MastodonAPIController, :user_statuses) - - get("/search", SearchController, :search) - - get("/statuses", MastodonAPIController, :get_statuses) - get("/statuses/:id", MastodonAPIController, :get_status) - get("/statuses/:id/context", MastodonAPIController, :get_context) - get("/statuses/:id/card", MastodonAPIController, :status_card) - get("/statuses/:id/favourited_by", MastodonAPIController, :favourited_by) - get("/statuses/:id/reblogged_by", MastodonAPIController, :reblogged_by) - - get("/pleroma/accounts/:id/favourites", MastodonAPIController, :user_favourites) - post( "/pleroma/accounts/confirmation_resend", MastodonAPIController, :account_confirmation_resend ) + + get("/timelines/public", MastodonAPIController, :public_timeline) + get("/timelines/tag/:tag", MastodonAPIController, :hashtag_timeline) + get("/timelines/list/:list_id", MastodonAPIController, :list_timeline) + + get("/statuses", MastodonAPIController, :get_statuses) + get("/statuses/:id", MastodonAPIController, :get_status) + get("/statuses/:id/context", MastodonAPIController, :get_context) + + get("/polls/:id", MastodonAPIController, :get_poll) + + get("/accounts/:id/statuses", MastodonAPIController, :user_statuses) + get("/accounts/:id/followers", MastodonAPIController, :followers) + get("/accounts/:id/following", MastodonAPIController, :following) + get("/accounts/:id", MastodonAPIController, :user) + + get("/search", SearchController, :search) + + get("/pleroma/accounts/:id/favourites", MastodonAPIController, :user_favourites) end scope "/api/v2", Pleroma.Web.MastodonAPI do @@ -435,6 +438,7 @@ defmodule Pleroma.Web.Router do get("/oauth_tokens", TwitterAPI.Controller, :oauth_tokens) delete("/oauth_tokens/:id", TwitterAPI.Controller, :revoke_token) + post("/qvitter/statuses/notifications/read", TwitterAPI.Controller, :notifications_read) end From b17f217bf3a16da0e98e63b59da22f40beec809d Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Tue, 17 Sep 2019 23:31:05 +0300 Subject: [PATCH 12/37] [#1234] Addressed code analysis issue. --- test/plugs/oauth_scopes_plug_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/plugs/oauth_scopes_plug_test.exs b/test/plugs/oauth_scopes_plug_test.exs index 3b895a6e4..c69e2de4f 100644 --- a/test/plugs/oauth_scopes_plug_test.exs +++ b/test/plugs/oauth_scopes_plug_test.exs @@ -5,8 +5,8 @@ defmodule Pleroma.Plugs.OAuthScopesPlugTest do use Pleroma.Web.ConnCase, async: true - alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Repo import Mock From 9f28249dfb0918cfd97450494c6c306dae9ab3fb Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Wed, 18 Sep 2019 12:06:12 +0300 Subject: [PATCH 13/37] [#1260] Added changelog entry. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4eb72c002..1a86881c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -116,6 +116,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Admin API: Added moderation log - Web response cache (currently, enabled for ActivityPub) - Mastodon API: Added an endpoint to get multiple statuses by IDs (`GET /api/v1/statuses/?ids[]=1&ids[]=2`) +- Authentication: Added rate limit for password-authorized actions / login existence checks. ### Changed - Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text From 551f2fa59eec11c94eb0ab187c06ccab7b8d5647 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Wed, 18 Sep 2019 12:31:33 +0300 Subject: [PATCH 14/37] [#1234] Added changelog entry, removed admin OAuth scopes. --- CHANGELOG.md | 1 + .../web/admin_api/admin_api_controller.ex | 16 ++++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 584386136..7e2c8066f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -117,6 +117,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Admin API: Added moderation log - Web response cache (currently, enabled for ActivityPub) - Mastodon API: Added an endpoint to get multiple statuses by IDs (`GET /api/v1/statuses/?ids[]=1&ids[]=2`) +- OAuth: support for hierarchical permissions / [Mastodon 2.4.3 OAuth permissions](https://docs.joinmastodon.org/api/permissions/) ### Changed - Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index 2c9840580..7f1a8e566 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -26,13 +26,13 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do plug( OAuthScopesPlug, - %{scopes: ["admin:read:accounts", "read:accounts"]} + %{scopes: ["read:accounts"]} when action in [:list_users, :user_show, :right_get, :invites] ) plug( OAuthScopesPlug, - %{scopes: ["admin:write", "write:accounts"]} + %{scopes: ["write:accounts"]} when action in [ :get_invite_token, :revoke_invite, @@ -53,35 +53,35 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do plug( OAuthScopesPlug, - %{scopes: ["admin:read:reports", "read:reports"]} when action in [:list_reports, :report_show] + %{scopes: ["read:reports"]} when action in [:list_reports, :report_show] ) plug( OAuthScopesPlug, - %{scopes: ["admin:write:reports", "write:reports"]} + %{scopes: ["write:reports"]} when action in [:report_update_state, :report_respond] ) plug( OAuthScopesPlug, - %{scopes: ["admin:read:statuses", "read:statuses"]} when action == :list_user_statuses + %{scopes: ["read:statuses"]} when action == :list_user_statuses ) plug( OAuthScopesPlug, - %{scopes: ["admin:write:statuses", "write:statuses"]} + %{scopes: ["write:statuses"]} when action in [:status_update, :status_delete] ) plug( OAuthScopesPlug, - %{scopes: ["admin:read", "read"]} + %{scopes: ["read"]} when action in [:config_show, :migrate_to_db, :migrate_from_db, :list_log] ) plug( OAuthScopesPlug, - %{scopes: ["admin:write", "write"]} + %{scopes: ["write"]} when action in [:relay_follow, :relay_unfollow, :config_update] ) From e4f3d7f69d5d3db787f8b4906cacc673e2f970f1 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Wed, 18 Sep 2019 10:31:10 +0000 Subject: [PATCH 15/37] Apply suggestion to lib/pleroma/plugs/oauth_scopes_plug.ex --- lib/pleroma/plugs/oauth_scopes_plug.ex | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/pleroma/plugs/oauth_scopes_plug.ex b/lib/pleroma/plugs/oauth_scopes_plug.ex index e0d61c4eb..a3278dbef 100644 --- a/lib/pleroma/plugs/oauth_scopes_plug.ex +++ b/lib/pleroma/plugs/oauth_scopes_plug.ex @@ -61,9 +61,10 @@ def filter_descendants(scopes, supported_scopes) do end defp maybe_perform_instance_privacy_check(%Plug.Conn{} = conn, options) do - case options[:skip_instance_privacy_check] do - true -> conn - _ -> EnsurePublicOrAuthenticatedPlug.call(conn, []) + if options[:skip_instance_privacy_check] do + conn + else + EnsurePublicOrAuthenticatedPlug.call(conn, []) end end end From f364438032b387624ced6b53ec0666738aa199ca Mon Sep 17 00:00:00 2001 From: kPherox Date: Tue, 1 Oct 2019 02:24:06 +0900 Subject: [PATCH 16/37] Add `rel="ugc"` --- .../controllers/account_controller/update_credentials_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs b/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs index cdbacf46b..599cd61c8 100644 --- a/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs +++ b/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs @@ -362,7 +362,7 @@ test "update fields", %{conn: conn} do assert account["fields"] == [ %{"name" => "foo", "value" => "bar"}, - %{"name" => "link", "value" => "cofe.io"} + %{"name" => "link", "value" => ~S(cofe.io)} ] assert account["source"]["fields"] == [ From 7f2bc577250ccea26c133502787d0bbdcbc839ee Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Tue, 1 Oct 2019 17:15:58 +0700 Subject: [PATCH 17/37] Move `follows`, `mutes` and `blocks` actions to AccountController --- .../controllers/account_controller.ex | 22 +++++++++++++ .../controllers/mastodon_api_controller.ex | 33 ------------------- lib/pleroma/web/router.ex | 6 ++-- .../controllers/account_controller_test.exs | 30 +++++++++++++++++ .../mastodon_api_controller_test.exs | 30 ----------------- 5 files changed, 55 insertions(+), 66 deletions(-) diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index df14ad66f..d02e35bc8 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -301,4 +301,26 @@ def unblock(%{assigns: %{user: blocker, account: blocked}} = conn, _params) do {:error, message} -> json_response(conn, :forbidden, %{error: message}) end end + + @doc "POST /api/v1/follows" + def follows(%{assigns: %{user: follower}} = conn, %{"uri" => uri}) do + with {_, %User{} = followed} <- {:followed, User.get_cached_by_nickname(uri)}, + {_, true} <- {:followed, follower.id != followed.id}, + {:ok, follower, followed, _} <- CommonAPI.follow(follower, followed) do + render(conn, "show.json", user: followed, for: follower) + else + {:followed, _} -> {:error, :not_found} + {:error, message} -> json_response(conn, :forbidden, %{error: message}) + end + end + + @doc "GET /api/v1/mutes" + def mutes(%{assigns: %{user: user}} = conn, _) do + render(conn, "index.json", users: User.muted_users(user), for: user, as: :user) + end + + @doc "GET /api/v1/blocks" + def blocks(%{assigns: %{user: user}} = conn, _) do + render(conn, "index.json", users: User.blocked_users(user), for: user, as: :user) + end end diff --git a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex index e92f5d089..9b62867b4 100644 --- a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex @@ -11,7 +11,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do alias Pleroma.Pagination alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.CommonAPI alias Pleroma.Web.MastodonAPI.AccountView alias Pleroma.Web.MastodonAPI.StatusView @@ -19,38 +18,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do action_fallback(Pleroma.Web.MastodonAPI.FallbackController) - def follows(%{assigns: %{user: follower}} = conn, %{"uri" => uri}) do - with {_, %User{} = followed} <- {:followed, User.get_cached_by_nickname(uri)}, - {_, true} <- {:followed, follower.id != followed.id}, - {:ok, follower, followed, _} <- CommonAPI.follow(follower, followed) do - conn - |> put_view(AccountView) - |> render("show.json", %{user: followed, for: follower}) - else - {:followed, _} -> - {:error, :not_found} - - {:error, message} -> - conn - |> put_status(:forbidden) - |> json(%{error: message}) - end - end - - def mutes(%{assigns: %{user: user}} = conn, _) do - with muted_accounts <- User.muted_users(user) do - res = AccountView.render("index.json", users: muted_accounts, for: user, as: :user) - json(conn, res) - end - end - - def blocks(%{assigns: %{user: user}} = conn, _) do - with blocked_accounts <- User.blocked_users(user) do - res = AccountView.render("index.json", users: blocked_accounts, for: user, as: :user) - json(conn, res) - end - end - def favourites(%{assigns: %{user: user}} = conn, params) do params = params diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index f91af8137..c6389b44e 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -347,8 +347,8 @@ defmodule Pleroma.Web.Router do get("/accounts/:id/identity_proofs", MastodonAPIController, :empty_array) get("/follow_requests", FollowRequestController, :index) - get("/blocks", MastodonAPIController, :blocks) - get("/mutes", MastodonAPIController, :mutes) + get("/blocks", AccountController, :blocks) + get("/mutes", AccountController, :mutes) get("/timelines/home", TimelineController, :home) get("/timelines/direct", TimelineController, :direct) @@ -426,7 +426,7 @@ defmodule Pleroma.Web.Router do scope [] do pipe_through(:oauth_follow) - post("/follows", MastodonAPIController, :follows) + post("/follows", AccountController, :follows) post("/accounts/:id/follow", AccountController, :follow) post("/accounts/:id/unfollow", AccountController, :unfollow) post("/accounts/:id/block", AccountController, :block) diff --git a/test/web/mastodon_api/controllers/account_controller_test.exs b/test/web/mastodon_api/controllers/account_controller_test.exs index 8c8017838..6a59c3d94 100644 --- a/test/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/web/mastodon_api/controllers/account_controller_test.exs @@ -849,4 +849,34 @@ test "returns an empty list on a bad request", %{conn: conn} do assert [] = json_response(conn, 200) end end + + test "getting a list of mutes", %{conn: conn} do + user = insert(:user) + other_user = insert(:user) + + {:ok, user} = User.mute(user, other_user) + + conn = + conn + |> assign(:user, user) + |> get("/api/v1/mutes") + + other_user_id = to_string(other_user.id) + assert [%{"id" => ^other_user_id}] = json_response(conn, 200) + end + + test "getting a list of blocks", %{conn: conn} do + user = insert(:user) + other_user = insert(:user) + + {:ok, user} = User.block(user, other_user) + + conn = + conn + |> assign(:user, user) + |> get("/api/v1/blocks") + + other_user_id = to_string(other_user.id) + assert [%{"id" => ^other_user_id}] = json_response(conn, 200) + end end diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index c03003dac..0e5f78f04 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -20,36 +20,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do clear_config([:rich_media, :enabled]) - test "getting a list of mutes", %{conn: conn} do - user = insert(:user) - other_user = insert(:user) - - {:ok, user} = User.mute(user, other_user) - - conn = - conn - |> assign(:user, user) - |> get("/api/v1/mutes") - - other_user_id = to_string(other_user.id) - assert [%{"id" => ^other_user_id}] = json_response(conn, 200) - end - - test "getting a list of blocks", %{conn: conn} do - user = insert(:user) - other_user = insert(:user) - - {:ok, user} = User.block(user, other_user) - - conn = - conn - |> assign(:user, user) - |> get("/api/v1/blocks") - - other_user_id = to_string(other_user.id) - assert [%{"id" => ^other_user_id}] = json_response(conn, 200) - end - test "unimplemented follow_requests, blocks, domain blocks" do user = insert(:user) From e0c68eeb024c7d91b098c4e0eb6ad57551ea9ef1 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Wed, 2 Oct 2019 18:27:01 +0700 Subject: [PATCH 18/37] Move `:favourites` and `:bookmarks` actions to StatusController --- .../controllers/mastodon_api_controller.ex | 43 ----------------- .../controllers/status_controller.ex | 37 +++++++++++++- lib/pleroma/web/router.ex | 4 +- .../controllers/status_controller_test.exs | 47 ++++++++++++++++++ .../mastodon_api_controller_test.exs | 48 ------------------- 5 files changed, 85 insertions(+), 94 deletions(-) diff --git a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex index 9b62867b4..7d839a8cf 100644 --- a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex @@ -5,53 +5,10 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do use Pleroma.Web, :controller - import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2] - - alias Pleroma.Bookmark - alias Pleroma.Pagination - alias Pleroma.User - alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.MastodonAPI.AccountView - alias Pleroma.Web.MastodonAPI.StatusView - require Logger action_fallback(Pleroma.Web.MastodonAPI.FallbackController) - def favourites(%{assigns: %{user: user}} = conn, params) do - params = - params - |> Map.put("type", "Create") - |> Map.put("favorited_by", user.ap_id) - |> Map.put("blocking_user", user) - - activities = - ActivityPub.fetch_activities([], params) - |> Enum.reverse() - - conn - |> add_link_headers(activities) - |> put_view(StatusView) - |> render("index.json", %{activities: activities, for: user, as: :activity}) - end - - def bookmarks(%{assigns: %{user: user}} = conn, params) do - user = User.get_cached_by_id(user.id) - - bookmarks = - Bookmark.for_user_query(user.id) - |> Pagination.fetch_paginated(params) - - activities = - bookmarks - |> Enum.map(fn b -> Map.put(b.activity, :bookmark, Map.delete(b, :activity)) end) - - conn - |> add_link_headers(bookmarks) - |> put_view(StatusView) - |> render("index.json", %{activities: activities, for: user, as: :activity}) - end - # Stubs for unimplemented mastodon api # def empty_array(conn, _) do diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex index 79cced163..973334b60 100644 --- a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex @@ -5,7 +5,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do use Pleroma.Web, :controller - import Pleroma.Web.ControllerHelper, only: [try_render: 3] + import Pleroma.Web.ControllerHelper, only: [try_render: 3, add_link_headers: 2] require Ecto.Query @@ -283,4 +283,39 @@ def context(%{assigns: %{user: user}} = conn, %{"id" => id}) do render(conn, "context.json", activity: activity, activities: activities, user: user) end end + + @doc "GET /api/v1/favourites" + def favourites(%{assigns: %{user: user}} = conn, params) do + params = + params + |> Map.put("type", "Create") + |> Map.put("favorited_by", user.ap_id) + |> Map.put("blocking_user", user) + + activities = + ActivityPub.fetch_activities([], params) + |> Enum.reverse() + + conn + |> add_link_headers(activities) + |> render("index.json", activities: activities, for: user, as: :activity) + end + + @doc "GET /api/v1/bookmarks" + def bookmarks(%{assigns: %{user: user}} = conn, params) do + user = User.get_cached_by_id(user.id) + + bookmarks = + user.id + |> Bookmark.for_user_query() + |> Pleroma.Pagination.fetch_paginated(params) + + activities = + bookmarks + |> Enum.map(fn b -> Map.put(b.activity, :bookmark, Map.delete(b, :activity)) end) + + conn + |> add_link_headers(bookmarks) + |> render("index.json", %{activities: activities, for: user, as: :activity}) + end end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index c6389b44e..c20d01c20 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -353,8 +353,8 @@ defmodule Pleroma.Web.Router do get("/timelines/home", TimelineController, :home) get("/timelines/direct", TimelineController, :direct) - get("/favourites", MastodonAPIController, :favourites) - get("/bookmarks", MastodonAPIController, :bookmarks) + get("/favourites", StatusController, :favourites) + get("/bookmarks", StatusController, :bookmarks) get("/notifications", NotificationController, :index) get("/notifications/:id", NotificationController, :show) diff --git a/test/web/mastodon_api/controllers/status_controller_test.exs b/test/web/mastodon_api/controllers/status_controller_test.exs index b648ad6ff..a4bbfe055 100644 --- a/test/web/mastodon_api/controllers/status_controller_test.exs +++ b/test/web/mastodon_api/controllers/status_controller_test.exs @@ -1242,4 +1242,51 @@ test "context" do "descendants" => [%{"id" => ^id4}, %{"id" => ^id5}] } = response end + + test "returns the favorites of a user", %{conn: conn} do + user = insert(:user) + other_user = insert(:user) + + {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"}) + {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"}) + + {:ok, _, _} = CommonAPI.favorite(activity.id, user) + + first_conn = + conn + |> assign(:user, user) + |> get("/api/v1/favourites") + + assert [status] = json_response(first_conn, 200) + assert status["id"] == to_string(activity.id) + + assert [{"link", _link_header}] = + Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end) + + # Honours query params + {:ok, second_activity} = + CommonAPI.post(other_user, %{ + "status" => + "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful." + }) + + {:ok, _, _} = CommonAPI.favorite(second_activity.id, user) + + last_like = status["id"] + + second_conn = + conn + |> assign(:user, user) + |> get("/api/v1/favourites?since_id=#{last_like}") + + assert [second_status] = json_response(second_conn, 200) + assert second_status["id"] == to_string(second_activity.id) + + third_conn = + conn + |> assign(:user, user) + |> get("/api/v1/favourites?limit=0") + + assert [] = json_response(third_conn, 200) + end end diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index 0e5f78f04..42a8779c0 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -7,7 +7,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do alias Pleroma.Notification alias Pleroma.Repo - alias Pleroma.User alias Pleroma.Web.CommonAPI import Pleroma.Factory @@ -34,53 +33,6 @@ test "unimplemented follow_requests, blocks, domain blocks" do end) end - test "returns the favorites of a user", %{conn: conn} do - user = insert(:user) - other_user = insert(:user) - - {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"}) - {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"}) - - {:ok, _, _} = CommonAPI.favorite(activity.id, user) - - first_conn = - conn - |> assign(:user, user) - |> get("/api/v1/favourites") - - assert [status] = json_response(first_conn, 200) - assert status["id"] == to_string(activity.id) - - assert [{"link", _link_header}] = - Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end) - - # Honours query params - {:ok, second_activity} = - CommonAPI.post(other_user, %{ - "status" => - "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful." - }) - - {:ok, _, _} = CommonAPI.favorite(second_activity.id, user) - - last_like = status["id"] - - second_conn = - conn - |> assign(:user, user) - |> get("/api/v1/favourites?since_id=#{last_like}") - - assert [second_status] = json_response(second_conn, 200) - assert second_status["id"] == to_string(second_activity.id) - - third_conn = - conn - |> assign(:user, user) - |> get("/api/v1/favourites?limit=0") - - assert [] = json_response(third_conn, 200) - end - describe "link headers" do test "preserves parameters in link headers", %{conn: conn} do user = insert(:user) From 977e711e22dd5171cbff241c7e763de8d34eef26 Mon Sep 17 00:00:00 2001 From: Thibaut Girka Date: Wed, 2 Oct 2019 13:18:51 +0200 Subject: [PATCH 19/37] Import object from self-Announce whenever possible --- .../web/activity_pub/transmogrifier.ex | 25 ++++++++++++- test/fixtures/mastodon-announce-private.json | 35 +++++++++++++++++++ test/support/http_request_mock.ex | 8 +++++ test/web/activity_pub/transmogrifier_test.exs | 20 ++++++++++- 4 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 test/fixtures/mastodon-announce-private.json diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 64c470fc8..8f9bf5525 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -580,7 +580,7 @@ def handle_incoming( ) do with actor <- Containment.get_actor(data), {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor), - {:ok, object} <- get_obj_helper(object_id), + {:ok, object} <- get_embedded_obj_helper(object_id, actor), public <- Visibility.is_public?(data), {:ok, activity, _object} <- ActivityPub.announce(actor, object, id, false, public) do {:ok, activity} @@ -781,6 +781,29 @@ def get_obj_helper(id, options \\ []) do end end + @spec get_embedded_obj_helper(String.t() | Object.t(), User.t()) :: {:ok, Object.t()} | nil + def get_embedded_obj_helper(%{"attributedTo" => attributedTo, "id" => object_id} = data, %User{ + ap_id: ap_id + }) + when attributedTo == ap_id do + with {:ok, activity} <- + handle_incoming(%{ + "type" => "Create", + "to" => data["to"], + "cc" => data["cc"], + "actor" => data["attributedTo"], + "object" => data + }) do + {:ok, Object.normalize(activity)} + else + _ -> get_obj_helper(object_id) + end + end + + def get_embedded_obj_helper(object_id, _) do + get_obj_helper(object_id) + end + def set_reply_to_uri(%{"inReplyTo" => in_reply_to} = object) when is_binary(in_reply_to) do with false <- String.starts_with?(in_reply_to, "http"), {:ok, %{data: replied_to_object}} <- get_obj_helper(in_reply_to) do diff --git a/test/fixtures/mastodon-announce-private.json b/test/fixtures/mastodon-announce-private.json new file mode 100644 index 000000000..9b868b13d --- /dev/null +++ b/test/fixtures/mastodon-announce-private.json @@ -0,0 +1,35 @@ +{ + "type": "Announce", + "to": [ + "http://mastodon.example.org/users/admin/followers" + ], + "published": "2018-02-17T19:39:15Z", + "object": { + "type": "Note", + "id": "http://mastodon.example.org/@admin/99541947525187368", + "attributedTo": "http://mastodon.example.org/users/admin", + "content": "this is a private toot", + "to": [ + "http://mastodon.example.org/users/admin/followers" + ] + }, + "id": "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity", + "atomUri": "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity", + "actor": "http://mastodon.example.org/users/admin", + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + { + "toot": "http://joinmastodon.org/ns#", + "sensitive": "as:sensitive", + "ostatus": "http://ostatus.org#", + "movedTo": "as:movedTo", + "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", + "inReplyToAtomUri": "ostatus:inReplyToAtomUri", + "conversation": "ostatus:conversation", + "atomUri": "ostatus:atomUri", + "Hashtag": "as:Hashtag", + "Emoji": "toot:Emoji" + } + ] +} diff --git a/test/support/http_request_mock.ex b/test/support/http_request_mock.ex index 5506c0626..7a87a2b35 100644 --- a/test/support/http_request_mock.ex +++ b/test/support/http_request_mock.ex @@ -349,6 +349,14 @@ def get( }} end + def get("http://mastodon.example.org/@admin/99541947525187368", _, _, _) do + {:ok, + %Tesla.Env{ + status: 404, + body: "" + }} + end + def get("https://shitposter.club/notice/7369654", _, _, _) do {:ok, %Tesla.Env{ diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs index b995f0224..d25334104 100644 --- a/test/web/activity_pub/transmogrifier_test.exs +++ b/test/web/activity_pub/transmogrifier_test.exs @@ -442,6 +442,25 @@ test "it works for incoming announces with an existing activity" do assert Activity.get_create_by_object_ap_id(data["object"]).id == activity.id end + test "it works for incoming announces with an inlined activity" do + data = + File.read!("test/fixtures/mastodon-announce-private.json") + |> Poison.decode!() + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["actor"] == "http://mastodon.example.org/users/admin" + assert data["type"] == "Announce" + + assert data["id"] == + "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity" + + object = Object.normalize(data["object"]) + + assert object.data["id"] == "http://mastodon.example.org/@admin/99541947525187368" + assert object.data["content"] == "this is a private toot" + end + test "it does not clobber the addressing on announce activities" do user = insert(:user) {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"}) @@ -1084,7 +1103,6 @@ test "it inlines private announced objects" do {:ok, announce_activity, _} = CommonAPI.repeat(activity.id, user) {:ok, modified} = Transmogrifier.prepare_outgoing(announce_activity.data) - object = modified["object"] assert modified["object"]["content"] == "hey" assert modified["object"]["actor"] == modified["object"]["attributedTo"] From 791d93ff0ecb304e3278b786e66689521e9ad19d Mon Sep 17 00:00:00 2001 From: Thibaut Girka Date: Wed, 2 Oct 2019 13:46:06 +0200 Subject: [PATCH 20/37] Make credo happy --- lib/pleroma/web/activity_pub/transmogrifier.ex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 8f9bf5525..449c777dc 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -782,16 +782,16 @@ def get_obj_helper(id, options \\ []) do end @spec get_embedded_obj_helper(String.t() | Object.t(), User.t()) :: {:ok, Object.t()} | nil - def get_embedded_obj_helper(%{"attributedTo" => attributedTo, "id" => object_id} = data, %User{ + def get_embedded_obj_helper(%{"attributedTo" => attributed_to, "id" => object_id} = data, %User{ ap_id: ap_id }) - when attributedTo == ap_id do + when attributed_to == ap_id do with {:ok, activity} <- handle_incoming(%{ "type" => "Create", "to" => data["to"], "cc" => data["cc"], - "actor" => data["attributedTo"], + "actor" => attributed_to, "object" => data }) do {:ok, Object.normalize(activity)} From b1ff66dc5f0f3915152314677bcd2379be0dd43b Mon Sep 17 00:00:00 2001 From: Thibaut Girka Date: Sat, 5 Oct 2019 12:46:06 +0200 Subject: [PATCH 21/37] Add test for handling Announces with inlined object from different origin --- test/fixtures/bogus-mastodon-announce.json | 43 +++++++++++++++++++ test/support/http_request_mock.ex | 8 ++++ test/web/activity_pub/transmogrifier_test.exs | 8 ++++ 3 files changed, 59 insertions(+) create mode 100644 test/fixtures/bogus-mastodon-announce.json diff --git a/test/fixtures/bogus-mastodon-announce.json b/test/fixtures/bogus-mastodon-announce.json new file mode 100644 index 000000000..0485b80b9 --- /dev/null +++ b/test/fixtures/bogus-mastodon-announce.json @@ -0,0 +1,43 @@ +{ + "type": "Announce", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "published": "2018-02-17T19:39:15Z", + "object": { + "type": "Note", + "id": "https://mastodon.social/users/emelie/statuses/101849165031453404", + "attributedTo": "https://mastodon.social/users/emelie", + "content": "this is a public toot", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "cc": [ + "https://mastodon.social/users/emelie", + "https://mastodon.social/users/emelie/followers" + ] + }, + "id": "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity", + "cc": [ + "http://mastodon.example.org/users/admin", + "http://mastodon.example.org/users/admin/followers" + ], + "atomUri": "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity", + "actor": "http://mastodon.example.org/users/admin", + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + { + "toot": "http://joinmastodon.org/ns#", + "sensitive": "as:sensitive", + "ostatus": "http://ostatus.org#", + "movedTo": "as:movedTo", + "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", + "inReplyToAtomUri": "ostatus:inReplyToAtomUri", + "conversation": "ostatus:conversation", + "atomUri": "ostatus:atomUri", + "Hashtag": "as:Hashtag", + "Emoji": "toot:Emoji" + } + ] +} diff --git a/test/support/http_request_mock.ex b/test/support/http_request_mock.ex index 7a87a2b35..b825a9307 100644 --- a/test/support/http_request_mock.ex +++ b/test/support/http_request_mock.ex @@ -46,6 +46,14 @@ def get("https://mastodon.social/users/emelie/statuses/101849165031453009", _, _ }} end + def get("https://mastodon.social/users/emelie/statuses/101849165031453404", _, _, _) do + {:ok, + %Tesla.Env{ + status: 404, + body: "" + }} + end + def get("https://mastodon.social/users/emelie", _, _, _) do {:ok, %Tesla.Env{ diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs index d25334104..02f606709 100644 --- a/test/web/activity_pub/transmogrifier_test.exs +++ b/test/web/activity_pub/transmogrifier_test.exs @@ -461,6 +461,14 @@ test "it works for incoming announces with an inlined activity" do assert object.data["content"] == "this is a private toot" end + test "it rejects incoming announces with an inlined activity from another origin" do + data = + File.read!("test/fixtures/bogus-mastodon-announce.json") + |> Poison.decode!() + + assert :error = Transmogrifier.handle_incoming(data) + end + test "it does not clobber the addressing on announce activities" do user = insert(:user) {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"}) From 8249924485965148f3a690c5d76e5f8b3bd78940 Mon Sep 17 00:00:00 2001 From: eugenijm Date: Wed, 2 Oct 2019 00:37:08 +0300 Subject: [PATCH 22/37] Mastodon API: Add `pleroma.unread_conversation_count` to the Account entity --- CHANGELOG.md | 1 + docs/API/differences_in_mastoapi_responses.md | 1 + lib/pleroma/conversation.ex | 2 + lib/pleroma/conversation/participation.ex | 17 +++ lib/pleroma/user.ex | 56 +++++++++ lib/pleroma/user/info.ex | 1 + .../web/mastodon_api/views/account_view.ex | 11 ++ ...unread_conversation_count_to_user_info.exs | 11 ++ test/conversation/participation_test.exs | 4 + .../conversation_controller_test.exs | 109 +++++++++++++++--- .../mastodon_api/views/account_view_test.exs | 21 ++++ .../pleroma_api_controller_test.exs | 2 + 12 files changed, 222 insertions(+), 14 deletions(-) create mode 100644 priv/repo/migrations/20191005165212_add_unread_conversation_count_to_user_info.exs diff --git a/CHANGELOG.md b/CHANGELOG.md index b42b13018..db505591b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Pleroma API: `GET /api/v1/pleroma/accounts/:id/scrobbles` to get a list of recently scrobbled items - Pleroma API: `POST /api/v1/pleroma/scrobble` to scrobble a media item - Mastodon API: Add `upload_limit`, `avatar_upload_limit`, `background_upload_limit`, and `banner_upload_limit` to `/api/v1/instance` +- Mastodon API: Add `pleroma.unread_conversation_count` to the Account entity ### Changed - **Breaking:** Elixir >=1.8 is now required (was >= 1.7) diff --git a/docs/API/differences_in_mastoapi_responses.md b/docs/API/differences_in_mastoapi_responses.md index d007a69c3..21b297529 100644 --- a/docs/API/differences_in_mastoapi_responses.md +++ b/docs/API/differences_in_mastoapi_responses.md @@ -56,6 +56,7 @@ Has these additional fields under the `pleroma` object: - `settings_store`: A generic map of settings for frontends. Opaque to the backend. Only returned in `verify_credentials` and `update_credentials` - `chat_token`: The token needed for Pleroma chat. Only returned in `verify_credentials` - `deactivated`: boolean, true when the user is deactivated +- `unread_conversation_count`: The count of unread conversations. Only returned to the account owner. ### Source diff --git a/lib/pleroma/conversation.ex b/lib/pleroma/conversation.ex index be5821ad7..098016af2 100644 --- a/lib/pleroma/conversation.ex +++ b/lib/pleroma/conversation.ex @@ -67,6 +67,8 @@ def create_or_bump_for(activity, opts \\ []) do participations = Enum.map(users, fn user -> + User.increment_unread_conversation_count(conversation, user) + {:ok, participation} = Participation.create_for_user_and_conversation(user, conversation, opts) diff --git a/lib/pleroma/conversation/participation.ex b/lib/pleroma/conversation/participation.ex index e946f6de2..ab81f3217 100644 --- a/lib/pleroma/conversation/participation.ex +++ b/lib/pleroma/conversation/participation.ex @@ -52,6 +52,15 @@ def mark_as_read(participation) do participation |> read_cng(%{read: true}) |> Repo.update() + |> case do + {:ok, participation} -> + participation = Repo.preload(participation, :user) + User.set_unread_conversation_count(participation.user) + {:ok, participation} + + error -> + error + end end def mark_as_unread(participation) do @@ -135,4 +144,12 @@ def set_recipients(participation, user_ids) do {:ok, Repo.preload(participation, :recipients, force: true)} end + + def unread_conversation_count_for_user(user) do + from(p in __MODULE__, + where: p.user_id == ^user.id, + where: not p.read, + select: %{count: count(p.id)} + ) + end end diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index c2f8fa0d7..0d665afa6 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -11,6 +11,7 @@ defmodule Pleroma.User do alias Comeonin.Pbkdf2 alias Ecto.Multi alias Pleroma.Activity + alias Pleroma.Conversation.Participation alias Pleroma.Delivery alias Pleroma.Keys alias Pleroma.Notification @@ -842,6 +843,61 @@ def maybe_update_following_count(%User{local: false} = user) do def maybe_update_following_count(user), do: user + def set_unread_conversation_count(%User{local: true} = user) do + unread_query = Participation.unread_conversation_count_for_user(user) + + User + |> join(:inner, [u], p in subquery(unread_query)) + |> update([u, p], + set: [ + info: + fragment( + "jsonb_set(?, '{unread_conversation_count}', ?::varchar::jsonb, true)", + u.info, + p.count + ) + ] + ) + |> where([u], u.id == ^user.id) + |> select([u], u) + |> Repo.update_all([]) + |> case do + {1, [user]} -> set_cache(user) + _ -> {:error, user} + end + end + + def set_unread_conversation_count(_), do: :noop + + def increment_unread_conversation_count(conversation, %User{local: true} = user) do + unread_query = + Participation.unread_conversation_count_for_user(user) + |> where([p], p.conversation_id == ^conversation.id) + + User + |> join(:inner, [u], p in subquery(unread_query)) + |> update([u, p], + set: [ + info: + fragment( + "jsonb_set(?, '{unread_conversation_count}', (coalesce((?->>'unread_conversation_count')::int, 0) + 1)::varchar::jsonb, true)", + u.info, + u.info + ) + ] + ) + |> where([u], u.id == ^user.id) + |> where([u, p], p.count == 0) + |> select([u], u) + |> Repo.update_all([]) + |> case do + {1, [user]} -> set_cache(user) + _ -> {:error, user} + end + end + + def increment_unread_conversation_count(_, _), do: :noop + def remove_duplicated_following(%User{following: following} = user) do uniq_following = Enum.uniq(following) diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex index ebd4ddebf..4b5b43d7f 100644 --- a/lib/pleroma/user/info.ex +++ b/lib/pleroma/user/info.ex @@ -47,6 +47,7 @@ defmodule Pleroma.User.Info do field(:hide_followers, :boolean, default: false) field(:hide_follows, :boolean, default: false) field(:hide_favorites, :boolean, default: true) + field(:unread_conversation_count, :integer, default: 0) field(:pinned_activities, {:array, :string}, default: []) field(:email_notifications, :map, default: %{"digest" => false}) field(:mascot, :map, default: nil) diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index 99169ef95..2d4976891 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -167,6 +167,7 @@ defp do_render("show.json", %{user: user} = opts) do |> maybe_put_chat_token(user, opts[:for], opts) |> maybe_put_activation_status(user, opts[:for]) |> maybe_put_follow_requests_count(user, opts[:for]) + |> maybe_put_unread_conversation_count(user, opts[:for]) end defp username_from_nickname(string) when is_binary(string) do @@ -248,6 +249,16 @@ defp maybe_put_activation_status(data, user, %User{info: %{is_admin: true}}) do defp maybe_put_activation_status(data, _, _), do: data + defp maybe_put_unread_conversation_count(data, %User{id: user_id} = user, %User{id: user_id}) do + data + |> Kernel.put_in( + [:pleroma, :unread_conversation_count], + user.info.unread_conversation_count + ) + end + + defp maybe_put_unread_conversation_count(data, _, _), do: data + defp image_url(%{"url" => [%{"href" => href} | _]}), do: href defp image_url(_), do: nil end diff --git a/priv/repo/migrations/20191005165212_add_unread_conversation_count_to_user_info.exs b/priv/repo/migrations/20191005165212_add_unread_conversation_count_to_user_info.exs new file mode 100644 index 000000000..2aa1a012c --- /dev/null +++ b/priv/repo/migrations/20191005165212_add_unread_conversation_count_to_user_info.exs @@ -0,0 +1,11 @@ +defmodule Pleroma.Repo.Migrations.AddUnreadConversationCountToUserInfo do + use Ecto.Migration + + def up do + execute(""" + update users set info = jsonb_set(info, '{unread_conversation_count}', 0::varchar::jsonb, true) where local=true + """) + end + + def down, do: :ok +end diff --git a/test/conversation/participation_test.exs b/test/conversation/participation_test.exs index a27167d42..f430bdf75 100644 --- a/test/conversation/participation_test.exs +++ b/test/conversation/participation_test.exs @@ -6,6 +6,7 @@ defmodule Pleroma.Conversation.ParticipationTest do use Pleroma.DataCase import Pleroma.Factory alias Pleroma.Conversation.Participation + alias Pleroma.User alias Pleroma.Web.CommonAPI test "getting a participation will also preload things" do @@ -30,6 +31,8 @@ test "for a new conversation, it sets the recipents of the participation" do {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @#{other_user.nickname}.", "visibility" => "direct"}) + user = User.get_cached_by_id(user.id) + other_user = User.get_cached_by_id(user.id) [participation] = Participation.for_user(user) participation = Pleroma.Repo.preload(participation, :recipients) @@ -155,6 +158,7 @@ test "it sets recipients, always keeping the owner of the participation even whe [participation] = Participation.for_user_with_last_activity_id(user) participation = Repo.preload(participation, :recipients) + user = User.get_cached_by_id(user.id) assert participation.recipients |> length() == 1 assert user in participation.recipients diff --git a/test/web/mastodon_api/controllers/conversation_controller_test.exs b/test/web/mastodon_api/controllers/conversation_controller_test.exs index 7117fc76a..a308a7620 100644 --- a/test/web/mastodon_api/controllers/conversation_controller_test.exs +++ b/test/web/mastodon_api/controllers/conversation_controller_test.exs @@ -10,19 +10,23 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do import Pleroma.Factory - test "Conversations", %{conn: conn} do + test "returns a list of conversations", %{conn: conn} do user_one = insert(:user) user_two = insert(:user) user_three = insert(:user) {:ok, user_two} = User.follow(user_two, user_one) + assert User.get_cached_by_id(user_two.id).info.unread_conversation_count == 0 + {:ok, direct} = CommonAPI.post(user_one, %{ "status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!", "visibility" => "direct" }) + assert User.get_cached_by_id(user_two.id).info.unread_conversation_count == 1 + {:ok, _follower_only} = CommonAPI.post(user_one, %{ "status" => "Hi @#{user_two.nickname}!", @@ -52,23 +56,100 @@ test "Conversations", %{conn: conn} do assert is_binary(res_id) assert unread == true assert res_last_status["id"] == direct.id + assert User.get_cached_by_id(user_one.id).info.unread_conversation_count == 1 + end + + test "updates the last_status on reply", %{conn: conn} do + user_one = insert(:user) + user_two = insert(:user) + + {:ok, direct} = + CommonAPI.post(user_one, %{ + "status" => "Hi @#{user_two.nickname}", + "visibility" => "direct" + }) + + {:ok, direct_reply} = + CommonAPI.post(user_two, %{ + "status" => "reply", + "visibility" => "direct", + "in_reply_to_status_id" => direct.id + }) + + [%{"last_status" => res_last_status}] = + conn + |> assign(:user, user_one) + |> get("/api/v1/conversations") + |> json_response(200) + + assert res_last_status["id"] == direct_reply.id + end + + test "the user marks a conversation as read", %{conn: conn} do + user_one = insert(:user) + user_two = insert(:user) + + {:ok, direct} = + CommonAPI.post(user_one, %{ + "status" => "Hi @#{user_two.nickname}", + "visibility" => "direct" + }) + + [%{"id" => direct_conversation_id, "unread" => true}] = + conn + |> assign(:user, user_one) + |> get("/api/v1/conversations") + |> json_response(200) + + %{"unread" => false} = + conn + |> assign(:user, user_one) + |> post("/api/v1/conversations/#{direct_conversation_id}/read") + |> json_response(200) + + assert User.get_cached_by_id(user_one.id).info.unread_conversation_count == 0 + + # The conversation is marked as unread on reply + {:ok, _} = + CommonAPI.post(user_two, %{ + "status" => "reply", + "visibility" => "direct", + "in_reply_to_status_id" => direct.id + }) + + [%{"unread" => true}] = + conn + |> assign(:user, user_one) + |> get("/api/v1/conversations") + |> json_response(200) + + assert User.get_cached_by_id(user_one.id).info.unread_conversation_count == 1 + + # A reply doesn't increment the user's unread_conversation_count if the conversation is unread + {:ok, _} = + CommonAPI.post(user_two, %{ + "status" => "reply", + "visibility" => "direct", + "in_reply_to_status_id" => direct.id + }) + + assert User.get_cached_by_id(user_one.id).info.unread_conversation_count == 1 + end + + test "(vanilla) Mastodon frontend behaviour", %{conn: conn} do + user_one = insert(:user) + user_two = insert(:user) + + {:ok, direct} = + CommonAPI.post(user_one, %{ + "status" => "Hi @#{user_two.nickname}!", + "visibility" => "direct" + }) - # Apparently undocumented API endpoint res_conn = conn |> assign(:user, user_one) - |> post("/api/v1/conversations/#{res_id}/read") - - assert response = json_response(res_conn, 200) - assert length(response["accounts"]) == 2 - assert response["last_status"]["id"] == direct.id - assert response["unread"] == false - - # (vanilla) Mastodon frontend behaviour - res_conn = - conn - |> assign(:user, user_one) - |> get("/api/v1/statuses/#{res_last_status["id"]}/context") + |> get("/api/v1/statuses/#{direct.id}/context") assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200) end diff --git a/test/web/mastodon_api/views/account_view_test.exs b/test/web/mastodon_api/views/account_view_test.exs index 62b2ab7e3..b7a4938a6 100644 --- a/test/web/mastodon_api/views/account_view_test.exs +++ b/test/web/mastodon_api/views/account_view_test.exs @@ -418,6 +418,27 @@ test "shows actual follower/following count to the account owner" do following_count: 1 } = AccountView.render("show.json", %{user: user, for: user}) end + + test "shows unread_conversation_count only to the account owner" do + user = insert(:user) + other_user = insert(:user) + + {:ok, _activity} = + CommonAPI.post(user, %{ + "status" => "Hey @#{other_user.nickname}.", + "visibility" => "direct" + }) + + user = User.get_cached_by_ap_id(user.ap_id) + + assert AccountView.render("show.json", %{user: user, for: other_user})[:pleroma][ + :unread_conversation_count + ] == nil + + assert AccountView.render("show.json", %{user: user, for: user})[:pleroma][ + :unread_conversation_count + ] == 1 + end end describe "follow requests counter" do diff --git a/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs b/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs index 7eaeda4a0..8a6528cbb 100644 --- a/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs +++ b/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs @@ -8,6 +8,7 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do alias Pleroma.Conversation.Participation alias Pleroma.Notification alias Pleroma.Repo + alias Pleroma.User alias Pleroma.Web.CommonAPI import Pleroma.Factory @@ -73,6 +74,7 @@ test "PATCH /api/v1/pleroma/conversations/:id", %{conn: conn} do participation = Repo.preload(participation, :recipients) + user = User.get_cached_by_id(user.id) assert [user] == participation.recipients assert other_user not in participation.recipients From fefbd31c6e60e25778ed01c522273375d4f04266 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Sun, 6 Oct 2019 16:22:35 +0300 Subject: [PATCH 23/37] Move local keys out of `user.info` --- lib/pleroma/signature.ex | 2 +- lib/pleroma/user.ex | 8 ++++++-- lib/pleroma/web/activity_pub/views/user_view.ex | 4 ++-- lib/pleroma/web/salmon/salmon.ex | 4 ++-- priv/repo/migrations/20191006123824_add_keys_column.exs | 9 +++++++++ .../20191006135457_move_keys_to_separate_column.exs | 7 +++++++ test/signature_test.exs | 5 ++--- test/user_test.exs | 8 ++++---- 8 files changed, 33 insertions(+), 14 deletions(-) create mode 100644 priv/repo/migrations/20191006123824_add_keys_column.exs create mode 100644 priv/repo/migrations/20191006135457_move_keys_to_separate_column.exs diff --git a/lib/pleroma/signature.ex b/lib/pleroma/signature.ex index f20aeb0d5..1e7c9ae86 100644 --- a/lib/pleroma/signature.ex +++ b/lib/pleroma/signature.ex @@ -48,7 +48,7 @@ def refetch_public_key(conn) do end def sign(%User{} = user, headers) do - with {:ok, %{info: %{keys: keys}}} <- User.ensure_keys_present(user), + with {:ok, %{keys: keys}} <- User.ensure_keys_present(user), {:ok, private_key, _} <- Keys.keys_from_pem(keys) do HTTPSignatures.sign(private_key, user.ap_id <> "#main-key", headers) end diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 0d665afa6..2cfb13a8c 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -51,6 +51,7 @@ defmodule Pleroma.User do field(:password_hash, :string) field(:password, :string, virtual: true) field(:password_confirmation, :string, virtual: true) + field(:keys, :string) field(:following, {:array, :string}, default: []) field(:ap_id, :string) field(:avatar, :map) @@ -1554,11 +1555,14 @@ def get_mascot(%{info: %{mascot: mascot}}) when is_nil(mascot) do } end - def ensure_keys_present(%{info: %{keys: keys}} = user) when not is_nil(keys), do: {:ok, user} + def ensure_keys_present(%{keys: keys} = user) when not is_nil(keys), do: {:ok, user} def ensure_keys_present(%User{} = user) do with {:ok, pem} <- Keys.generate_rsa_pem() do - update_info(user, &User.Info.set_keys(&1, pem)) + user + |> cast(%{keys: pem}, [:keys]) + |> validate_required([:keys]) + |> update_and_set_cache() end end diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex index 6bc55c85b..9b39d1629 100644 --- a/lib/pleroma/web/activity_pub/views/user_view.ex +++ b/lib/pleroma/web/activity_pub/views/user_view.ex @@ -33,7 +33,7 @@ def render("endpoints.json", _), do: %{} def render("service.json", %{user: user}) do {:ok, user} = User.ensure_keys_present(user) - {:ok, _, public_key} = Keys.keys_from_pem(user.info.keys) + {:ok, _, public_key} = Keys.keys_from_pem(user.keys) public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key) public_key = :public_key.pem_encode([public_key]) @@ -69,7 +69,7 @@ def render("user.json", %{user: %User{nickname: "internal." <> _} = user}), def render("user.json", %{user: user}) do {:ok, user} = User.ensure_keys_present(user) - {:ok, _, public_key} = Keys.keys_from_pem(user.info.keys) + {:ok, _, public_key} = Keys.keys_from_pem(user.keys) public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key) public_key = :public_key.pem_encode([public_key]) diff --git a/lib/pleroma/web/salmon/salmon.ex b/lib/pleroma/web/salmon/salmon.ex index 8ba7380c0..0ffe903cd 100644 --- a/lib/pleroma/web/salmon/salmon.ex +++ b/lib/pleroma/web/salmon/salmon.ex @@ -202,7 +202,7 @@ def is_representable?(_), do: false @spec publish(User.t(), Pleroma.Activity.t()) :: none def publish(user, activity) - def publish(%{info: %{keys: keys}} = user, %{data: %{"type" => type}} = activity) + def publish(%{keys: keys} = user, %{data: %{"type" => type}} = activity) when type in @supported_activities do feed = ActivityRepresenter.to_simple_form(activity, user, true) @@ -238,7 +238,7 @@ def publish(%{info: %{keys: keys}} = user, %{data: %{"type" => type}} = activity def publish(%{id: id}, _), do: Logger.debug(fn -> "Keys missing for user #{id}" end) def gather_webfinger_links(%User{} = user) do - {:ok, _private, public} = Keys.keys_from_pem(user.info.keys) + {:ok, _private, public} = Keys.keys_from_pem(user.keys) magic_key = encode_key(public) [ diff --git a/priv/repo/migrations/20191006123824_add_keys_column.exs b/priv/repo/migrations/20191006123824_add_keys_column.exs new file mode 100644 index 000000000..b6c615646 --- /dev/null +++ b/priv/repo/migrations/20191006123824_add_keys_column.exs @@ -0,0 +1,9 @@ +defmodule Pleroma.Repo.Migrations.AddKeysColumn do + use Ecto.Migration + + def change do + alter table("users") do + add_if_not_exists :keys, :text + end + end +end diff --git a/priv/repo/migrations/20191006135457_move_keys_to_separate_column.exs b/priv/repo/migrations/20191006135457_move_keys_to_separate_column.exs new file mode 100644 index 000000000..504dde53a --- /dev/null +++ b/priv/repo/migrations/20191006135457_move_keys_to_separate_column.exs @@ -0,0 +1,7 @@ +defmodule Pleroma.Repo.Migrations.MoveKeysToSeparateColumn do + use Ecto.Migration + + def change do + execute("update users set keys = info->>'keys' where local", "update users set info = jsonb_set(info, '{keys}'::text[], to_jsonb(keys)) where local") + end +end diff --git a/test/signature_test.exs b/test/signature_test.exs index d5bf63d7d..96c8ba07a 100644 --- a/test/signature_test.exs +++ b/test/signature_test.exs @@ -80,7 +80,7 @@ test "it returns signature headers" do user = insert(:user, %{ ap_id: "https://mastodon.social/users/lambadalambda", - info: %{keys: @private_key} + keys: @private_key }) assert Signature.sign( @@ -94,8 +94,7 @@ test "it returns signature headers" do end test "it returns error" do - user = - insert(:user, %{ap_id: "https://mastodon.social/users/lambadalambda", info: %{keys: ""}}) + user = insert(:user, %{ap_id: "https://mastodon.social/users/lambadalambda", keys: ""}) assert Signature.sign( user, diff --git a/test/user_test.exs b/test/user_test.exs index 1bc853c94..ae21286e4 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -1457,15 +1457,15 @@ test "if user is unconfirmed" do describe "ensure_keys_present" do test "it creates keys for a user and stores them in info" do user = insert(:user) - refute is_binary(user.info.keys) + refute is_binary(user.keys) {:ok, user} = User.ensure_keys_present(user) - assert is_binary(user.info.keys) + assert is_binary(user.keys) end test "it doesn't create keys if there already are some" do - user = insert(:user, %{info: %{keys: "xxx"}}) + user = insert(:user, keys: "xxx") {:ok, user} = User.ensure_keys_present(user) - assert user.info.keys == "xxx" + assert user.keys == "xxx" end end From b93856874de673f1c05c557ad482d4480ca7e0a7 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Sun, 6 Oct 2019 17:12:17 +0300 Subject: [PATCH 24/37] [#1234] Merge remote-tracking branch 'remotes/upstream/develop' into 1234-mastodon-2-4-3-oauth-scopes # Conflicts: # CHANGELOG.md # lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex # lib/pleroma/web/router.ex --- .../activity_pub/activity_pub_controller.ex | 5 - lib/pleroma/web/masto_fe_controller.ex | 12 + .../controllers/account_controller.ex | 10 +- .../controllers/app_controller.ex | 3 + .../controllers/domain_block_controller.ex | 2 + .../controllers/follow_request_controller.ex | 2 + .../controllers/mastodon_api_controller.ex | 63 ---- .../controllers/media_controller.ex | 5 + .../controllers/notification_controller.ex | 2 + .../controllers/poll_controller.ex | 10 + .../controllers/report_controller.ex | 2 + .../controllers/search_controller.ex | 2 + .../controllers/status_controller.ex | 5 + .../controllers/subscription_controller.ex | 2 + .../controllers/suggestion_controller.ex | 5 + .../controllers/emoji_api_controller.ex | 18 ++ lib/pleroma/web/router.ex | 285 +++++++----------- .../controllers/util_controller.ex | 2 + 18 files changed, 186 insertions(+), 249 deletions(-) diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index 93fd9e248..080030eb5 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -30,11 +30,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do when action in [:activity, :object] ) - plug( - Pleroma.Plugs.OAuthScopesPlug, - %{scopes: ["read:accounts"]} when action in [:followers, :following] - ) - plug(Pleroma.Web.FederatingPlug when action in [:inbox, :relay]) plug(:set_requester_reachable when action in [:inbox]) plug(:relay_active? when action in [:relay]) diff --git a/lib/pleroma/web/masto_fe_controller.ex b/lib/pleroma/web/masto_fe_controller.ex index ac9af7502..87860f1d5 100644 --- a/lib/pleroma/web/masto_fe_controller.ex +++ b/lib/pleroma/web/masto_fe_controller.ex @@ -5,8 +5,20 @@ defmodule Pleroma.Web.MastoFEController do use Pleroma.Web, :controller + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User + plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action == :put_settings) + + # Note: :index action handles attempt of unauthenticated access to private instance with redirect + plug( + OAuthScopesPlug, + %{scopes: ["read"], fallback: :proceed_unauthenticated, skip_instance_privacy_check: true} + when action == :index + ) + + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action != :index) + @doc "GET /web/*path" def index(%{assigns: %{user: user}} = conn, _params) do token = get_session(conn, :oauth_token) diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index e195f56c4..9ef7fd48d 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -36,6 +36,11 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action == :lists) + plug( + OAuthScopesPlug, + %{scopes: ["follow", "read:blocks"]} when action == :blocks + ) + plug( OAuthScopesPlug, %{scopes: ["follow", "write:blocks"]} when action in [:block, :unblock] @@ -43,11 +48,14 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do plug(OAuthScopesPlug, %{scopes: ["read:follows"]} when action == :relationships) + # Note: :follows (POST /api/v1/follows) is the same as :follow, consider removing :follows plug( OAuthScopesPlug, - %{scopes: ["follow", "write:follows"]} when action in [:follow, :unfollow] + %{scopes: ["follow", "write:follows"]} when action in [:follows, :follow, :unfollow] ) + plug(OAuthScopesPlug, %{scopes: ["follow", "read:mutes"]} when action == :mutes) + plug(OAuthScopesPlug, %{scopes: ["follow", "write:mutes"]} when action in [:mute, :unmute]) plug( diff --git a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex index abbe16a88..13a30a34d 100644 --- a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex @@ -5,6 +5,7 @@ defmodule Pleroma.Web.MastodonAPI.AppController do use Pleroma.Web, :controller + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Repo alias Pleroma.Web.OAuth.App alias Pleroma.Web.OAuth.Scopes @@ -12,6 +13,8 @@ defmodule Pleroma.Web.MastodonAPI.AppController do action_fallback(Pleroma.Web.MastodonAPI.FallbackController) + plug(OAuthScopesPlug, %{scopes: ["read"]} when action == :verify_credentials) + @local_mastodon_name "Mastodon-Local" @doc "POST /api/v1/apps" diff --git a/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex b/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex index 45c5ef8a4..c7606246b 100644 --- a/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex @@ -18,6 +18,8 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockController do %{scopes: ["follow", "write:blocks"]} when action != :index ) + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + @doc "GET /api/v1/domain_blocks" def index(%{assigns: %{user: %{info: info}}} = conn, _) do json(conn, Map.get(info, :domain_blocks, [])) diff --git a/lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex b/lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex index 06672e2bb..3ccbdf1c6 100644 --- a/lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex @@ -21,6 +21,8 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestController do %{scopes: ["follow", "write:follows"]} when action != :index ) + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + @doc "GET /api/v1/follow_requests" def index(%{assigns: %{user: followed}} = conn, _params) do follow_requests = User.get_follow_requests(followed) diff --git a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex index 32077d420..7d839a8cf 100644 --- a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex @@ -7,69 +7,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do require Logger - alias Pleroma.Plugs.OAuthScopesPlug - @unauthenticated_access %{fallback: :proceed_unauthenticated, scopes: []} - - # Note: :index action handles attempt of unauthenticated access to private instance with redirect - plug( - OAuthScopesPlug, - Map.merge(@unauthenticated_access, %{scopes: ["read"], skip_instance_privacy_check: true}) - when action == :index - ) - - plug( - OAuthScopesPlug, - %{scopes: ["read"]} when action in [:suggestions, :verify_app_credentials] - ) - - plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action == :put_settings) - - plug( - OAuthScopesPlug, - %{@unauthenticated_access | scopes: ["read:statuses"]} when action == :get_poll - ) - - plug(OAuthScopesPlug, %{scopes: ["write:statuses"]} when action == :poll_vote) - - plug(OAuthScopesPlug, %{scopes: ["read:favourites"]} when action == :favourites) - - plug(OAuthScopesPlug, %{scopes: ["write:media"]} when action in [:upload, :update_media]) - - plug( - OAuthScopesPlug, - %{scopes: ["follow", "read:blocks"]} when action == :blocks - ) - - # To do: POST /api/v1/follows is not present in Mastodon; consider removing the action - plug( - OAuthScopesPlug, - %{scopes: ["follow", "write:follows"]} when action == :follows - ) - - plug(OAuthScopesPlug, %{scopes: ["follow", "read:mutes"]} when action == :mutes) - - # Note: scope not present in Mastodon: read:bookmarks - plug(OAuthScopesPlug, %{scopes: ["read:bookmarks"]} when action == :bookmarks) - - # An extra safety measure for possible actions not guarded by OAuth permissions specification - plug( - Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug - when action not in [ - :create_app, - :index, - :login, - :logout, - :password_reset, - :masto_instance, - :peers, - :custom_emojis - ] - ) - - plug(RateLimiter, :password_reset when action == :password_reset) - - @local_mastodon_name "Mastodon-Local" - action_fallback(Pleroma.Web.MastodonAPI.FallbackController) # Stubs for unimplemented mastodon api diff --git a/lib/pleroma/web/mastodon_api/controllers/media_controller.ex b/lib/pleroma/web/mastodon_api/controllers/media_controller.ex index 57a5b60fb..ed4c08d99 100644 --- a/lib/pleroma/web/mastodon_api/controllers/media_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/media_controller.ex @@ -6,12 +6,17 @@ defmodule Pleroma.Web.MastodonAPI.MediaController do use Pleroma.Web, :controller alias Pleroma.Object + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub action_fallback(Pleroma.Web.MastodonAPI.FallbackController) plug(:put_view, Pleroma.Web.MastodonAPI.StatusView) + plug(OAuthScopesPlug, %{scopes: ["write:media"]}) + + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + @doc "POST /api/v1/media" def create(%{assigns: %{user: user}} = conn, %{"file" => file} = data) do with {:ok, object} <- diff --git a/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex b/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex index 36c6defc2..16759be6a 100644 --- a/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex @@ -20,6 +20,8 @@ defmodule Pleroma.Web.MastodonAPI.NotificationController do plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action not in @oauth_read_actions) + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + # GET /api/v1/notifications def index(%{assigns: %{user: user}} = conn, params) do notifications = MastodonAPI.get_notifications(user, params) diff --git a/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex b/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex index fbf7f8673..d129f8672 100644 --- a/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex @@ -9,11 +9,21 @@ defmodule Pleroma.Web.MastodonAPI.PollController do alias Pleroma.Activity alias Pleroma.Object + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.CommonAPI action_fallback(Pleroma.Web.MastodonAPI.FallbackController) + plug( + OAuthScopesPlug, + %{scopes: ["read:statuses"], fallback: :proceed_unauthenticated} when action == :show + ) + + plug(OAuthScopesPlug, %{scopes: ["write:statuses"]} when action == :vote) + + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + @doc "GET /api/v1/polls/:id" def show(%{assigns: %{user: user}} = conn, %{"id" => id}) do with %Object{} = object <- Object.get_by_id_and_maybe_refetch(id, interval: 60), diff --git a/lib/pleroma/web/mastodon_api/controllers/report_controller.ex b/lib/pleroma/web/mastodon_api/controllers/report_controller.ex index 313f885a6..263c2180f 100644 --- a/lib/pleroma/web/mastodon_api/controllers/report_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/report_controller.ex @@ -11,6 +11,8 @@ defmodule Pleroma.Web.MastodonAPI.ReportController do plug(OAuthScopesPlug, %{scopes: ["write:reports"]} when action == :create) + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + @doc "POST /api/v1/reports" def create(%{assigns: %{user: user}} = conn, params) do with {:ok, activity} <- Pleroma.Web.CommonAPI.report(user, params) do diff --git a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex index 9f39b00f8..6cfd68a84 100644 --- a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex @@ -20,6 +20,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do # Note: Mastodon doesn't allow unauthenticated access (requires read:accounts / read:search) plug(OAuthScopesPlug, %{scopes: ["read:search"], fallback: :proceed_unauthenticated}) + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + plug(RateLimiter, :search when action in [:search, :search2, :account_search]) def account_search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex index 2cbf33046..0c16e9b0f 100644 --- a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex @@ -47,6 +47,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do ] ) + plug(OAuthScopesPlug, %{scopes: ["read:favourites"]} when action == :favourites) + plug( OAuthScopesPlug, %{scopes: ["write:favourites"]} when action in [:favourite, :unfavourite] @@ -65,6 +67,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action in [:pin, :unpin]) + # Note: scope not present in Mastodon: read:bookmarks + plug(OAuthScopesPlug, %{scopes: ["read:bookmarks"]} when action == :bookmarks) + # Note: scope not present in Mastodon: write:bookmarks plug( OAuthScopesPlug, diff --git a/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex b/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex index 287eebf92..fc7d52824 100644 --- a/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex @@ -14,6 +14,8 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionController do plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["push"]}) + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + # Creates PushSubscription # POST /api/v1/push/subscription # diff --git a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex index 9076bb849..fe71c36af 100644 --- a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex @@ -8,11 +8,16 @@ defmodule Pleroma.Web.MastodonAPI.SuggestionController do require Logger alias Pleroma.Config + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User alias Pleroma.Web.MediaProxy action_fallback(Pleroma.Web.MastodonAPI.FallbackController) + plug(OAuthScopesPlug, %{scopes: ["read"]} when action == :index) + + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + @doc "GET /api/v1/suggestions" def index(%{assigns: %{user: user}} = conn, _) do if Config.get([:suggestions, :enabled], false) do diff --git a/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex b/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex index 545ad80c9..a474d41d4 100644 --- a/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex @@ -1,8 +1,26 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do use Pleroma.Web, :controller + alias Pleroma.Plugs.OAuthScopesPlug + require Logger + plug( + OAuthScopesPlug, + %{scopes: ["write"]} + when action in [ + :create, + :delete, + :download_from, + :list_from, + :import_from_fs, + :update_file, + :update_metadata + ] + ) + + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + def emoji_dir_path do Path.join( Pleroma.Config.get!([:instance, :static_dir]), diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index a36c40a3b..675f485b2 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -87,31 +87,6 @@ defmodule Pleroma.Web.Router do plug(Pleroma.Plugs.EnsureUserKeyPlug) end - pipeline :oauth_read_or_public do - plug(Pleroma.Plugs.OAuthScopesPlug, %{ - scopes: ["read"], - fallback: :proceed_unauthenticated - }) - - plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) - end - - pipeline :oauth_read do - plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["read"]}) - end - - pipeline :oauth_write do - plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["write"]}) - end - - pipeline :oauth_follow do - plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["follow"]}) - end - - pipeline :oauth_push do - plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["push"]}) - end - pipeline :well_known do plug(:accepts, ["json", "jrd+json", "xml", "xrd+xml"]) end @@ -154,7 +129,7 @@ defmodule Pleroma.Web.Router do end scope "/api/pleroma/admin", Pleroma.Web.AdminAPI do - pipe_through([:admin_api, :oauth_write]) + pipe_through(:admin_api) post("/users/follow", AdminAPIController, :user_follow) post("/users/unfollow", AdminAPIController, :user_unfollow) @@ -213,7 +188,7 @@ defmodule Pleroma.Web.Router do scope "/api/pleroma/emoji", Pleroma.Web.PleromaAPI do scope "/packs" do # Modifying packs - pipe_through([:admin_api, :oauth_write]) + pipe_through(:admin_api) post("/import_from_fs", EmojiAPIController, :import_from_fs) @@ -238,31 +213,20 @@ defmodule Pleroma.Web.Router do post("/main/ostatus", UtilController, :remote_subscribe) get("/ostatus_subscribe", UtilController, :remote_follow) - scope [] do - pipe_through(:oauth_follow) - post("/ostatus_subscribe", UtilController, :do_remote_follow) - end + post("/ostatus_subscribe", UtilController, :do_remote_follow) end scope "/api/pleroma", Pleroma.Web.TwitterAPI do pipe_through(:authenticated_api) - scope [] do - pipe_through(:oauth_write) + post("/change_email", UtilController, :change_email) + post("/change_password", UtilController, :change_password) + post("/delete_account", UtilController, :delete_account) + put("/notification_settings", UtilController, :update_notificaton_settings) + post("/disable_account", UtilController, :disable_account) - post("/change_email", UtilController, :change_email) - post("/change_password", UtilController, :change_password) - post("/delete_account", UtilController, :delete_account) - put("/notification_settings", UtilController, :update_notificaton_settings) - post("/disable_account", UtilController, :disable_account) - end - - scope [] do - pipe_through(:oauth_follow) - - post("/blocks_import", UtilController, :blocks_import) - post("/follow_import", UtilController, :follow_import) - end + post("/blocks_import", UtilController, :blocks_import) + post("/follow_import", UtilController, :follow_import) end scope "/oauth", Pleroma.Web.OAuth do @@ -289,14 +253,14 @@ defmodule Pleroma.Web.Router do scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do scope [] do pipe_through(:authenticated_api) - pipe_through(:oauth_read) + get("/conversations/:id/statuses", PleromaAPIController, :conversation_statuses) get("/conversations/:id", PleromaAPIController, :conversation) end scope [] do pipe_through(:authenticated_api) - pipe_through(:oauth_write) + patch("/conversations/:id", PleromaAPIController, :update_conversation) post("/notifications/read", PleromaAPIController, :read_notification) @@ -312,13 +276,11 @@ defmodule Pleroma.Web.Router do scope [] do pipe_through(:api) - pipe_through(:oauth_read_or_public) get("/accounts/:id/favourites", AccountController, :favourites) end scope [] do pipe_through(:authenticated_api) - pipe_through(:oauth_follow) post("/accounts/:id/subscribe", AccountController, :subscribe) post("/accounts/:id/unsubscribe", AccountController, :unsubscribe) @@ -328,131 +290,114 @@ defmodule Pleroma.Web.Router do end scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do - pipe_through([:api, :oauth_read_or_public]) - + pipe_through(:api) get("/accounts/:id/scrobbles", ScrobbleController, :user_scrobbles) end scope "/api/v1", Pleroma.Web.MastodonAPI do pipe_through(:authenticated_api) - scope [] do - pipe_through(:oauth_read) + get("/accounts/verify_credentials", AccountController, :verify_credentials) - get("/accounts/verify_credentials", AccountController, :verify_credentials) + get("/accounts/relationships", AccountController, :relationships) - get("/accounts/relationships", AccountController, :relationships) + get("/accounts/:id/lists", AccountController, :lists) + get("/accounts/:id/identity_proofs", MastodonAPIController, :empty_array) - get("/accounts/:id/lists", AccountController, :lists) - get("/accounts/:id/identity_proofs", MastodonAPIController, :empty_array) + get("/follow_requests", FollowRequestController, :index) + get("/blocks", AccountController, :blocks) + get("/mutes", AccountController, :mutes) - get("/follow_requests", FollowRequestController, :index) - get("/blocks", AccountController, :blocks) - get("/mutes", AccountController, :mutes) + get("/timelines/home", TimelineController, :home) + get("/timelines/direct", TimelineController, :direct) - get("/timelines/home", TimelineController, :home) - get("/timelines/direct", TimelineController, :direct) + get("/favourites", StatusController, :favourites) + get("/bookmarks", StatusController, :bookmarks) - get("/favourites", StatusController, :favourites) - get("/bookmarks", StatusController, :bookmarks) + get("/notifications", NotificationController, :index) + get("/notifications/:id", NotificationController, :show) + post("/notifications/clear", NotificationController, :clear) + post("/notifications/dismiss", NotificationController, :dismiss) + delete("/notifications/destroy_multiple", NotificationController, :destroy_multiple) - get("/notifications", NotificationController, :index) - get("/notifications/:id", NotificationController, :show) - post("/notifications/clear", NotificationController, :clear) - post("/notifications/dismiss", NotificationController, :dismiss) - delete("/notifications/destroy_multiple", NotificationController, :destroy_multiple) + get("/scheduled_statuses", ScheduledActivityController, :index) + get("/scheduled_statuses/:id", ScheduledActivityController, :show) - get("/scheduled_statuses", ScheduledActivityController, :index) - get("/scheduled_statuses/:id", ScheduledActivityController, :show) + get("/lists", ListController, :index) + get("/lists/:id", ListController, :show) + get("/lists/:id/accounts", ListController, :list_accounts) - get("/lists", ListController, :index) - get("/lists/:id", ListController, :show) - get("/lists/:id/accounts", ListController, :list_accounts) + get("/domain_blocks", DomainBlockController, :index) - get("/domain_blocks", DomainBlockController, :index) + get("/filters", FilterController, :index) - get("/filters", FilterController, :index) + get("/suggestions", SuggestionController, :index) - get("/suggestions", SuggestionController, :index) + get("/conversations", ConversationController, :index) + post("/conversations/:id/read", ConversationController, :read) - get("/conversations", ConversationController, :index) - post("/conversations/:id/read", ConversationController, :read) + get("/endorsements", AccountController, :endorsements) - get("/endorsements", MastodonAPIController, :empty_array) - end + patch("/accounts/update_credentials", AccountController, :update_credentials) - scope [] do - pipe_through(:oauth_write) + post("/statuses", StatusController, :create) + delete("/statuses/:id", StatusController, :delete) - patch("/accounts/update_credentials", AccountController, :update_credentials) + post("/statuses/:id/reblog", StatusController, :reblog) + post("/statuses/:id/unreblog", StatusController, :unreblog) + post("/statuses/:id/favourite", StatusController, :favourite) + post("/statuses/:id/unfavourite", StatusController, :unfavourite) + post("/statuses/:id/pin", StatusController, :pin) + post("/statuses/:id/unpin", StatusController, :unpin) + post("/statuses/:id/bookmark", StatusController, :bookmark) + post("/statuses/:id/unbookmark", StatusController, :unbookmark) + post("/statuses/:id/mute", StatusController, :mute_conversation) + post("/statuses/:id/unmute", StatusController, :unmute_conversation) - post("/statuses", StatusController, :create) - delete("/statuses/:id", StatusController, :delete) + put("/scheduled_statuses/:id", ScheduledActivityController, :update) + delete("/scheduled_statuses/:id", ScheduledActivityController, :delete) - post("/statuses/:id/reblog", StatusController, :reblog) - post("/statuses/:id/unreblog", StatusController, :unreblog) - post("/statuses/:id/favourite", StatusController, :favourite) - post("/statuses/:id/unfavourite", StatusController, :unfavourite) - post("/statuses/:id/pin", StatusController, :pin) - post("/statuses/:id/unpin", StatusController, :unpin) - post("/statuses/:id/bookmark", StatusController, :bookmark) - post("/statuses/:id/unbookmark", StatusController, :unbookmark) - post("/statuses/:id/mute", StatusController, :mute_conversation) - post("/statuses/:id/unmute", StatusController, :unmute_conversation) + post("/polls/:id/votes", PollController, :vote) - put("/scheduled_statuses/:id", ScheduledActivityController, :update) - delete("/scheduled_statuses/:id", ScheduledActivityController, :delete) + post("/media", MediaController, :create) + put("/media/:id", MediaController, :update) - post("/polls/:id/votes", PollController, :vote) + delete("/lists/:id", ListController, :delete) + post("/lists", ListController, :create) + put("/lists/:id", ListController, :update) - post("/media", MediaController, :create) - put("/media/:id", MediaController, :update) + post("/lists/:id/accounts", ListController, :add_to_list) + delete("/lists/:id/accounts", ListController, :remove_from_list) - delete("/lists/:id", ListController, :delete) - post("/lists", ListController, :create) - put("/lists/:id", ListController, :update) + post("/filters", FilterController, :create) + get("/filters/:id", FilterController, :show) + put("/filters/:id", FilterController, :update) + delete("/filters/:id", FilterController, :delete) - post("/lists/:id/accounts", ListController, :add_to_list) - delete("/lists/:id/accounts", ListController, :remove_from_list) + post("/reports", ReportController, :create) - post("/filters", FilterController, :create) - get("/filters/:id", FilterController, :show) - put("/filters/:id", FilterController, :update) - delete("/filters/:id", FilterController, :delete) + post("/follows", AccountController, :follows) + post("/accounts/:id/follow", AccountController, :follow) + post("/accounts/:id/unfollow", AccountController, :unfollow) + post("/accounts/:id/block", AccountController, :block) + post("/accounts/:id/unblock", AccountController, :unblock) + post("/accounts/:id/mute", AccountController, :mute) + post("/accounts/:id/unmute", AccountController, :unmute) - post("/reports", ReportController, :create) - end + post("/follow_requests/:id/authorize", FollowRequestController, :authorize) + post("/follow_requests/:id/reject", FollowRequestController, :reject) - scope [] do - pipe_through(:oauth_follow) + post("/domain_blocks", DomainBlockController, :create) + delete("/domain_blocks", DomainBlockController, :delete) - post("/follows", AccountController, :follows) - post("/accounts/:id/follow", AccountController, :follow) - post("/accounts/:id/unfollow", AccountController, :unfollow) - post("/accounts/:id/block", AccountController, :block) - post("/accounts/:id/unblock", AccountController, :unblock) - post("/accounts/:id/mute", AccountController, :mute) - post("/accounts/:id/unmute", AccountController, :unmute) - - post("/follow_requests/:id/authorize", FollowRequestController, :authorize) - post("/follow_requests/:id/reject", FollowRequestController, :reject) - - post("/domain_blocks", DomainBlockController, :create) - delete("/domain_blocks", DomainBlockController, :delete) - end - - scope [] do - pipe_through(:oauth_push) - - post("/push/subscription", SubscriptionController, :create) - get("/push/subscription", SubscriptionController, :get) - put("/push/subscription", SubscriptionController, :update) - delete("/push/subscription", SubscriptionController, :delete) - end + post("/push/subscription", SubscriptionController, :create) + get("/push/subscription", SubscriptionController, :get) + put("/push/subscription", SubscriptionController, :update) + delete("/push/subscription", SubscriptionController, :delete) end scope "/api/web", Pleroma.Web do - pipe_through([:authenticated_api, :oauth_write]) + pipe_through(:authenticated_api) put("/settings", MastoFEController, :put_settings) end @@ -477,30 +422,26 @@ defmodule Pleroma.Web.Router do get("/trends", MastodonAPIController, :empty_array) - scope [] do - pipe_through(:oauth_read_or_public) + get("/timelines/public", TimelineController, :public) + get("/timelines/tag/:tag", TimelineController, :hashtag) + get("/timelines/list/:list_id", TimelineController, :list) - get("/timelines/public", TimelineController, :public) - get("/timelines/tag/:tag", TimelineController, :hashtag) - get("/timelines/list/:list_id", TimelineController, :list) + get("/statuses", StatusController, :index) + get("/statuses/:id", StatusController, :show) + get("/statuses/:id/context", StatusController, :context) - get("/statuses", StatusController, :index) - get("/statuses/:id", StatusController, :show) - get("/statuses/:id/context", StatusController, :context) + get("/polls/:id", PollController, :show) - get("/polls/:id", PollController, :show) + get("/accounts/:id/statuses", AccountController, :statuses) + get("/accounts/:id/followers", AccountController, :followers) + get("/accounts/:id/following", AccountController, :following) + get("/accounts/:id", AccountController, :show) - get("/accounts/:id/statuses", AccountController, :statuses) - get("/accounts/:id/followers", AccountController, :followers) - get("/accounts/:id/following", AccountController, :following) - get("/accounts/:id", AccountController, :show) - - get("/search", SearchController, :search) - end + get("/search", SearchController, :search) end scope "/api/v2", Pleroma.Web.MastodonAPI do - pipe_through([:api, :oauth_read_or_public]) + pipe_through(:api) get("/search", SearchController, :search2) end @@ -531,11 +472,7 @@ defmodule Pleroma.Web.Router do get("/oauth_tokens", TwitterAPI.Controller, :oauth_tokens) delete("/oauth_tokens/:id", TwitterAPI.Controller, :revoke_token) - scope [] do - pipe_through(:oauth_read) - - post("/qvitter/statuses/notifications/read", TwitterAPI.Controller, :notifications_read) - end + post("/qvitter/statuses/notifications/read", TwitterAPI.Controller, :notifications_read) end pipeline :ap_service_actor do @@ -599,23 +536,14 @@ defmodule Pleroma.Web.Router do scope "/", Pleroma.Web.ActivityPub do pipe_through([:activitypub_client]) - scope [] do - pipe_through(:oauth_read) - get("/api/ap/whoami", ActivityPubController, :whoami) - get("/users/:nickname/inbox", ActivityPubController, :read_inbox) - end + get("/api/ap/whoami", ActivityPubController, :whoami) + get("/users/:nickname/inbox", ActivityPubController, :read_inbox) - scope [] do - pipe_through(:oauth_write) - post("/users/:nickname/outbox", ActivityPubController, :update_outbox) - post("/api/ap/upload_media", ActivityPubController, :upload_media) - end + post("/users/:nickname/outbox", ActivityPubController, :update_outbox) + post("/api/ap/upload_media", ActivityPubController, :upload_media) - scope [] do - pipe_through(:oauth_read_or_public) - get("/users/:nickname/followers", ActivityPubController, :followers) - get("/users/:nickname/following", ActivityPubController, :following) - end + get("/users/:nickname/followers", ActivityPubController, :followers) + get("/users/:nickname/following", ActivityPubController, :following) end scope "/", Pleroma.Web.ActivityPub do @@ -665,10 +593,7 @@ defmodule Pleroma.Web.Router do post("/auth/password", MastodonAPI.AuthController, :password_reset) - scope [] do - pipe_through(:oauth_read) - get("/web/*path", MastoFEController, :index) - end + get("/web/*path", MastoFEController, :index) end pipeline :remote_media do diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index c84359ddb..2305bb413 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -39,6 +39,8 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do ] ) + plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action == :notifications_read) + plug(Pleroma.Plugs.SetFormatPlug when action in [:config, :version]) def help_test(conn, _params) do From 1d8e956c32e412283aed0cebf90df11121d897bb Mon Sep 17 00:00:00 2001 From: feld Date: Sun, 6 Oct 2019 14:13:04 +0000 Subject: [PATCH 25/37] Use the user.id instead of nickname in report URLs Ensures links to profiles of remote users work. --- CHANGELOG.md | 1 + lib/pleroma/emails/admin_email.ex | 2 +- test/emails/admin_email_test.exs | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db505591b..607643b7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Mastodon API: Fix private and direct statuses not being filtered out from the public timeline for an authenticated user (`GET /api/v1/timelines/public`) - Mastodon API: Inability to get some local users by nickname in `/api/v1/accounts/:id_or_nickname` - Added `:instance, extended_nickname_format` setting to the default config +- Report emails now include functional links to profiles of remote user accounts ## [1.1.0] - 2019-??-?? ### Security diff --git a/lib/pleroma/emails/admin_email.ex b/lib/pleroma/emails/admin_email.ex index c14be02dd..5a0903c13 100644 --- a/lib/pleroma/emails/admin_email.ex +++ b/lib/pleroma/emails/admin_email.ex @@ -17,7 +17,7 @@ defp instance_notify_email do end defp user_url(user) do - Helpers.o_status_url(Pleroma.Web.Endpoint, :feed_redirect, user.nickname) + Helpers.o_status_url(Pleroma.Web.Endpoint, :feed_redirect, user.id) end def report(to, reporter, account, statuses, comment) do diff --git a/test/emails/admin_email_test.exs b/test/emails/admin_email_test.exs index 31eac5f12..02c277a33 100644 --- a/test/emails/admin_email_test.exs +++ b/test/emails/admin_email_test.exs @@ -19,8 +19,8 @@ test "build report email" do AdminEmail.report(to_user, reporter, account, [%{name: "Test", id: "12"}], "Test comment") status_url = Helpers.o_status_url(Pleroma.Web.Endpoint, :notice, "12") - reporter_url = Helpers.o_status_url(Pleroma.Web.Endpoint, :feed_redirect, reporter.nickname) - account_url = Helpers.o_status_url(Pleroma.Web.Endpoint, :feed_redirect, account.nickname) + reporter_url = Helpers.o_status_url(Pleroma.Web.Endpoint, :feed_redirect, reporter.id) + account_url = Helpers.o_status_url(Pleroma.Web.Endpoint, :feed_redirect, account.id) assert res.to == [{to_user.name, to_user.email}] assert res.from == {config[:name], config[:notify_email]} From 125f96e7064aa0d714c91bf4a0c78c25734c64ef Mon Sep 17 00:00:00 2001 From: rinpatch Date: Sun, 6 Oct 2019 17:05:51 +0300 Subject: [PATCH 26/37] Fix the use of queries with a schema in a migration --- .../20190711042024_copy_muted_to_muted_notifications.exs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/priv/repo/migrations/20190711042024_copy_muted_to_muted_notifications.exs b/priv/repo/migrations/20190711042024_copy_muted_to_muted_notifications.exs index 50669902e..815d66549 100644 --- a/priv/repo/migrations/20190711042024_copy_muted_to_muted_notifications.exs +++ b/priv/repo/migrations/20190711042024_copy_muted_to_muted_notifications.exs @@ -1,15 +1,10 @@ defmodule Pleroma.Repo.Migrations.CopyMutedToMutedNotifications do use Ecto.Migration + import Ecto.Query alias Pleroma.User def change do - query = - User.Query.build(%{ - local: true, - active: true, - order_by: :id - }) - + query = from(u in "users", where: fragment("not (?->'deactivated' @> 'true')", u.info), select: %{info: u.info}, where: u.local == true, order_by: u.id) Pleroma.Repo.stream(query) |> Enum.each(fn %{info: %{mutes: mutes} = info} = user -> From ee88afb2e2d26defe18541dd2a57f8c34e191cb8 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Sun, 6 Oct 2019 17:53:03 +0300 Subject: [PATCH 27/37] Fix muted notification migration only working with a schema Should also improve performance. I tested it on my local DB, but if anyone has a backup of <=1.0 db they can test this on, please do --- ...0711042024_copy_muted_to_muted_notifications.exs | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/priv/repo/migrations/20190711042024_copy_muted_to_muted_notifications.exs b/priv/repo/migrations/20190711042024_copy_muted_to_muted_notifications.exs index 815d66549..b717cab2e 100644 --- a/priv/repo/migrations/20190711042024_copy_muted_to_muted_notifications.exs +++ b/priv/repo/migrations/20190711042024_copy_muted_to_muted_notifications.exs @@ -1,19 +1,8 @@ defmodule Pleroma.Repo.Migrations.CopyMutedToMutedNotifications do use Ecto.Migration - import Ecto.Query alias Pleroma.User def change do - query = from(u in "users", where: fragment("not (?->'deactivated' @> 'true')", u.info), select: %{info: u.info}, where: u.local == true, order_by: u.id) - Pleroma.Repo.stream(query) - |> Enum.each(fn - %{info: %{mutes: mutes} = info} = user -> - info_cng = - Ecto.Changeset.cast(info, %{muted_notifications: mutes}, [:muted_notifications]) - - Ecto.Changeset.change(user) - |> Ecto.Changeset.put_embed(:info, info_cng) - |> Pleroma.Repo.update() - end) + execute("update users set info = jsonb_set(info, '{muted_notifications}', info->'mutes', true) where local = true") end end From 58aeabd020ae7355aea6b63a331b164e8b23a6c0 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Mon, 7 Oct 2019 02:55:09 +0200 Subject: [PATCH 28/37] mrf/simple_policy: check actor against accept/reject --- CHANGELOG.md | 1 + .../web/activity_pub/mrf/simple_policy.ex | 4 ++- .../activity_pub/mrf/simple_policy_test.exs | 26 +++++++++++++++---- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 607643b7c..33321e990 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Admin API: Return `total` when querying for reports - Mastodon API: Return `pleroma.direct_conversation_id` when creating a direct message (`POST /api/v1/statuses`) - Admin API: Return link alongside with token on password reset +- MRF (Simple Policy): Also use `:accept`/`:reject` on the actors rather than only their activities ### Fixed - Mastodon API: Fix private and direct statuses not being filtered out from the public timeline for an authenticated user (`GET /api/v1/timelines/public`) diff --git a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex index 8aa6852f0..8e53296e7 100644 --- a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex @@ -168,7 +168,9 @@ def filter(%{"id" => actor, "type" => obj_type} = object) when obj_type in ["Application", "Group", "Organization", "Person", "Service"] do actor_info = URI.parse(actor) - with {:ok, object} <- check_avatar_removal(actor_info, object), + with {:ok, object} <- check_accept(actor_info, object), + {:ok, object} <- check_reject(actor_info, object), + {:ok, object} <- check_avatar_removal(actor_info, object), {:ok, object} <- check_banner_removal(actor_info, object) do {:ok, object} else diff --git a/test/web/activity_pub/mrf/simple_policy_test.exs b/test/web/activity_pub/mrf/simple_policy_test.exs index 7203b27da..df0f223f8 100644 --- a/test/web/activity_pub/mrf/simple_policy_test.exs +++ b/test/web/activity_pub/mrf/simple_policy_test.exs @@ -236,7 +236,7 @@ test "is empty" do assert SimplePolicy.filter(remote_message) == {:ok, remote_message} end - test "has a matching host" do + test "activity has a matching host" do Config.put([:mrf_simple, :reject], ["remote.instance"]) remote_message = build_remote_message() @@ -244,13 +244,21 @@ test "has a matching host" do assert SimplePolicy.filter(remote_message) == {:reject, nil} end - test "match with wildcard domain" do + test "activity matches with wildcard domain" do Config.put([:mrf_simple, :reject], ["*.remote.instance"]) remote_message = build_remote_message() assert SimplePolicy.filter(remote_message) == {:reject, nil} end + + test "actor has a matching host" do + Config.put([:mrf_simple, :reject], ["remote.instance"]) + + remote_user = build_remote_user() + + assert SimplePolicy.filter(remote_user) == {:reject, nil} + end end describe "when :accept" do @@ -264,7 +272,7 @@ test "is empty" do assert SimplePolicy.filter(remote_message) == {:ok, remote_message} end - test "is not empty but it doesn't have a matching host" do + test "is not empty but activity doesn't have a matching host" do Config.put([:mrf_simple, :accept], ["non.matching.remote"]) local_message = build_local_message() @@ -274,7 +282,7 @@ test "is not empty but it doesn't have a matching host" do assert SimplePolicy.filter(remote_message) == {:reject, nil} end - test "has a matching host" do + test "activity has a matching host" do Config.put([:mrf_simple, :accept], ["remote.instance"]) local_message = build_local_message() @@ -284,7 +292,7 @@ test "has a matching host" do assert SimplePolicy.filter(remote_message) == {:ok, remote_message} end - test "match with wildcard domain" do + test "activity matches with wildcard domain" do Config.put([:mrf_simple, :accept], ["*.remote.instance"]) local_message = build_local_message() @@ -293,6 +301,14 @@ test "match with wildcard domain" do assert SimplePolicy.filter(local_message) == {:ok, local_message} assert SimplePolicy.filter(remote_message) == {:ok, remote_message} end + + test "actor has a matching host" do + Config.put([:mrf_simple, :accept], ["remote.instance"]) + + remote_user = build_remote_user() + + assert SimplePolicy.filter(remote_user) == {:ok, remote_user} + end end describe "when :avatar_removal" do From 19962d2022f87498520655969ac99811205d048d Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Mon, 7 Oct 2019 11:33:58 +0000 Subject: [PATCH 29/37] clean up bundled litepub schema (closes #1303) --- priv/static/schemas/litepub-0.1.jsonld | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/priv/static/schemas/litepub-0.1.jsonld b/priv/static/schemas/litepub-0.1.jsonld index f01c2c33a..1cfcb7ec7 100644 --- a/priv/static/schemas/litepub-0.1.jsonld +++ b/priv/static/schemas/litepub-0.1.jsonld @@ -14,9 +14,8 @@ "discoverable": "toot:discoverable", "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", "ostatus": "http://ostatus.org#", - "schema": "http://schema.org", + "schema": "http://schema.org#", "toot": "http://joinmastodon.org/ns#", - "totalItems": "as:totalItems", "value": "schema:value", "sensitive": "as:sensitive", "litepub": "http://litepub.social/ns#", @@ -28,10 +27,6 @@ "oauthRegistrationEndpoint": { "@id": "litepub:oauthRegistrationEndpoint", "@type": "@id" - }, - "uploadMedia": { - "@id": "litepub:uploadMedia", - "@type": "@id" } } ] From 4b3f77a99ff849f7593f63f7dccd206683b34d97 Mon Sep 17 00:00:00 2001 From: Sergey Suprunenko Date: Mon, 7 Oct 2019 12:20:41 +0000 Subject: [PATCH 30/37] Extract RSS Feed functionality from OStatus --- CHANGELOG.md | 2 + config/config.exs | 3 +- docs/configuration/cheatsheet.md | 1 + lib/pleroma/emails/admin_email.ex | 2 +- lib/pleroma/web/feed/feed_controller.ex | 63 +++++ lib/pleroma/web/feed/feed_view.ex | 77 ++++++ lib/pleroma/web/metadata/feed.ex | 23 ++ lib/pleroma/web/ostatus/ostatus_controller.ex | 43 +--- lib/pleroma/web/router.ex | 5 +- .../web/templates/feed/feed/_activity.xml.eex | 48 ++++ .../web/templates/feed/feed/_author.xml.eex | 17 ++ .../web/templates/feed/feed/feed.xml.eex | 26 ++ test/emails/admin_email_test.exs | 4 +- test/user_test.exs | 4 +- test/web/feed/feed_controller_test.exs | 227 ++++++++++++++++++ test/web/metadata/feed_test.exs | 18 ++ test/web/ostatus/ostatus_controller_test.exs | 201 ---------------- 17 files changed, 513 insertions(+), 251 deletions(-) create mode 100644 lib/pleroma/web/feed/feed_controller.ex create mode 100644 lib/pleroma/web/feed/feed_view.ex create mode 100644 lib/pleroma/web/metadata/feed.ex create mode 100644 lib/pleroma/web/templates/feed/feed/_activity.xml.eex create mode 100644 lib/pleroma/web/templates/feed/feed/_author.xml.eex create mode 100644 lib/pleroma/web/templates/feed/feed/feed.xml.eex create mode 100644 test/web/feed/feed_controller_test.exs create mode 100644 test/web/metadata/feed_test.exs diff --git a/CHANGELOG.md b/CHANGELOG.md index f480a3f72..f193ec1ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Mastodon API: Add `pleroma.unread_conversation_count` to the Account entity - OAuth: support for hierarchical permissions / [Mastodon 2.4.3 OAuth permissions](https://docs.joinmastodon.org/api/permissions/) - Authentication: Added rate limit for password-authorized actions / login existence checks +- Metadata Link: Atom syndication Feed ### Changed - **Breaking:** Elixir >=1.8 is now required (was >= 1.7) @@ -25,6 +26,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Mastodon API: Return `pleroma.direct_conversation_id` when creating a direct message (`POST /api/v1/statuses`) - Admin API: Return link alongside with token on password reset - MRF (Simple Policy): Also use `:accept`/`:reject` on the actors rather than only their activities +- OStatus: Extract RSS functionality ### Fixed - Mastodon API: Fix private and direct statuses not being filtered out from the public timeline for an authenticated user (`GET /api/v1/timelines/public`) diff --git a/config/config.exs b/config/config.exs index cf94f1a19..f4d92102f 100644 --- a/config/config.exs +++ b/config/config.exs @@ -409,7 +409,8 @@ providers: [ Pleroma.Web.Metadata.Providers.OpenGraph, Pleroma.Web.Metadata.Providers.TwitterCard, - Pleroma.Web.Metadata.Providers.RelMe + Pleroma.Web.Metadata.Providers.RelMe, + Pleroma.Web.Metadata.Providers.Feed ], unfurl_nsfw: false diff --git a/docs/configuration/cheatsheet.md b/docs/configuration/cheatsheet.md index b86799ecc..8d85276ed 100644 --- a/docs/configuration/cheatsheet.md +++ b/docs/configuration/cheatsheet.md @@ -475,6 +475,7 @@ config :pleroma, :workers, * Pleroma.Web.Metadata.Providers.OpenGraph * Pleroma.Web.Metadata.Providers.TwitterCard * Pleroma.Web.Metadata.Providers.RelMe - add links from user bio with rel=me into the `
` as `` + * Pleroma.Web.Metadata.Providers.Feed - add a link to a user's Atom feed into the `
` as `` * `unfurl_nsfw`: If set to `true` nsfw attachments will be shown in previews ## :rich_media diff --git a/lib/pleroma/emails/admin_email.ex b/lib/pleroma/emails/admin_email.ex index 5a0903c13..b15e4041b 100644 --- a/lib/pleroma/emails/admin_email.ex +++ b/lib/pleroma/emails/admin_email.ex @@ -17,7 +17,7 @@ defp instance_notify_email do end defp user_url(user) do - Helpers.o_status_url(Pleroma.Web.Endpoint, :feed_redirect, user.id) + Helpers.feed_url(Pleroma.Web.Endpoint, :feed_redirect, user.id) end def report(to, reporter, account, statuses, comment) do diff --git a/lib/pleroma/web/feed/feed_controller.ex b/lib/pleroma/web/feed/feed_controller.ex new file mode 100644 index 000000000..d91ecef9c --- /dev/null +++ b/lib/pleroma/web/feed/feed_controller.ex @@ -0,0 +1,63 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Feed.FeedController do + use Pleroma.Web, :controller + + alias Fallback.RedirectController + alias Pleroma.User + alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.ActivityPub.ActivityPubController + + plug(Pleroma.Plugs.SetFormatPlug when action in [:feed_redirect]) + + action_fallback(:errors) + + def feed_redirect(%{assigns: %{format: "html"}} = conn, %{"nickname" => nickname}) do + with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname_or_id(nickname)} do + RedirectController.redirector_with_meta(conn, %{user: user}) + end + end + + def feed_redirect(%{assigns: %{format: format}} = conn, _params) + when format in ["json", "activity+json"] do + ActivityPubController.call(conn, :user) + end + + def feed_redirect(conn, %{"nickname" => nickname}) do + with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do + redirect(conn, external: "#{feed_url(conn, :feed, user.nickname)}.atom") + end + end + + def feed(conn, %{"nickname" => nickname} = params) do + with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do + query_params = + params + |> Map.take(["max_id"]) + |> Map.put("type", ["Create"]) + |> Map.put("whole_db", true) + |> Map.put("actor_id", user.ap_id) + + activities = + query_params + |> ActivityPub.fetch_public_activities() + |> Enum.reverse() + + conn + |> put_resp_content_type("application/atom+xml") + |> render("feed.xml", user: user, activities: activities) + end + end + + def errors(conn, {:error, :not_found}) do + render_error(conn, :not_found, "Not found") + end + + def errors(conn, {:fetch_user, nil}), do: errors(conn, {:error, :not_found}) + + def errors(conn, _) do + render_error(conn, :internal_server_error, "Something went wrong") + end +end diff --git a/lib/pleroma/web/feed/feed_view.ex b/lib/pleroma/web/feed/feed_view.ex new file mode 100644 index 000000000..5eef1e757 --- /dev/null +++ b/lib/pleroma/web/feed/feed_view.ex @@ -0,0 +1,77 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Feed.FeedView do + use Phoenix.HTML + use Pleroma.Web, :view + + alias Pleroma.Object + alias Pleroma.User + alias Pleroma.Web.MediaProxy + + require Pleroma.Constants + + def most_recent_update(activities, user) do + (List.first(activities) || user).updated_at + |> NaiveDateTime.to_iso8601() + end + + def logo(user) do + user + |> User.avatar_url() + |> MediaProxy.url() + end + + def last_activity(activities) do + List.last(activities) + end + + def activity_object(activity) do + Object.normalize(activity) + end + + def activity_object_data(activity) do + activity + |> activity_object() + |> Map.get(:data) + end + + def activity_content(activity) do + content = activity_object_data(activity)["content"] + + content + |> String.replace(~r/[\n\r]/, "") + |> escape() + end + + def activity_context(activity) do + activity.data["context"] + end + + def attachment_href(attachment) do + attachment["url"] + |> hd() + |> Map.get("href") + end + + def attachment_type(attachment) do + attachment["url"] + |> hd() + |> Map.get("mediaType") + end + + def get_href(id) do + with %Object{data: %{"external_url" => external_url}} <- Object.get_cached_by_ap_id(id) do + external_url + else + _e -> id + end + end + + def escape(html) do + html + |> html_escape() + |> safe_to_string() + end +end diff --git a/lib/pleroma/web/metadata/feed.ex b/lib/pleroma/web/metadata/feed.ex new file mode 100644 index 000000000..8043e6c54 --- /dev/null +++ b/lib/pleroma/web/metadata/feed.ex @@ -0,0 +1,23 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Metadata.Providers.Feed do + alias Pleroma.Web.Endpoint + alias Pleroma.Web.Metadata.Providers.Provider + alias Pleroma.Web.Router.Helpers + + @behaviour Provider + + @impl Provider + def build_tags(%{user: user}) do + [ + {:link, + [ + rel: "alternate", + type: "application/atom+xml", + href: Helpers.feed_path(Endpoint, :feed, user.nickname) <> ".atom" + ], []} + ] + end +end diff --git a/lib/pleroma/web/ostatus/ostatus_controller.ex b/lib/pleroma/web/ostatus/ostatus_controller.ex index 8f325b28e..20f2d9ddc 100644 --- a/lib/pleroma/web/ostatus/ostatus_controller.ex +++ b/lib/pleroma/web/ostatus/ostatus_controller.ex @@ -9,16 +9,13 @@ defmodule Pleroma.Web.OStatus.OStatusController do alias Pleroma.Activity alias Pleroma.Object alias Pleroma.User - alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPubController alias Pleroma.Web.ActivityPub.ObjectView alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.Endpoint alias Pleroma.Web.Federator alias Pleroma.Web.Metadata.PlayerView - alias Pleroma.Web.OStatus alias Pleroma.Web.OStatus.ActivityRepresenter - alias Pleroma.Web.OStatus.FeedRepresenter alias Pleroma.Web.Router alias Pleroma.Web.XML @@ -31,49 +28,11 @@ defmodule Pleroma.Web.OStatus.OStatusController do plug( Pleroma.Plugs.SetFormatPlug - when action in [:feed_redirect, :object, :activity, :notice] + when action in [:object, :activity, :notice] ) action_fallback(:errors) - def feed_redirect(%{assigns: %{format: "html"}} = conn, %{"nickname" => nickname}) do - with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname_or_id(nickname)} do - RedirectController.redirector_with_meta(conn, %{user: user}) - end - end - - def feed_redirect(%{assigns: %{format: format}} = conn, _params) - when format in ["json", "activity+json"] do - ActivityPubController.call(conn, :user) - end - - def feed_redirect(conn, %{"nickname" => nickname}) do - with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do - redirect(conn, external: OStatus.feed_path(user)) - end - end - - def feed(conn, %{"nickname" => nickname} = params) do - with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do - activities = - params - |> Map.take(["max_id"]) - |> Map.merge(%{"whole_db" => true, "actor_id" => user.ap_id}) - |> ActivityPub.fetch_public_activities() - |> Enum.reverse() - - response = - user - |> FeedRepresenter.to_simple_form(activities, [user]) - |> :xmerl.export_simple(:xmerl_xml) - |> to_string - - conn - |> put_resp_content_type("application/atom+xml") - |> send_resp(200, response) - end - end - defp decode_or_retry(body) do with {:ok, magic_key} <- Pleroma.Web.Salmon.fetch_magic_key(body), {:ok, doc} <- Pleroma.Web.Salmon.decode_and_validate(magic_key, body) do diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 675f485b2..ae799b8ac 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -495,8 +495,9 @@ defmodule Pleroma.Web.Router do get("/activities/:uuid", OStatus.OStatusController, :activity) get("/notice/:id", OStatus.OStatusController, :notice) get("/notice/:id/embed_player", OStatus.OStatusController, :notice_player) - get("/users/:nickname/feed", OStatus.OStatusController, :feed) - get("/users/:nickname", OStatus.OStatusController, :feed_redirect) + + get("/users/:nickname/feed", Feed.FeedController, :feed) + get("/users/:nickname", Feed.FeedController, :feed_redirect) post("/users/:nickname/salmon", OStatus.OStatusController, :salmon_incoming) post("/push/hub/:nickname", Websub.WebsubController, :websub_subscription_request) diff --git a/lib/pleroma/web/templates/feed/feed/_activity.xml.eex b/lib/pleroma/web/templates/feed/feed/_activity.xml.eex new file mode 100644 index 000000000..d1f5e903c --- /dev/null +++ b/lib/pleroma/web/templates/feed/feed/_activity.xml.eex @@ -0,0 +1,48 @@ + + http://activitystrea.ms/schema/1.0/note + http://activitystrea.ms/schema/1.0/post + <%= @data["id"] %> + <%= "New note by #{@user.nickname}" %> + <%= activity_content(@activity) %> + <%= @data["published"] %> + <%= @data["published"] %> + <%= activity_context(@activity) %> + + + <%= if @data["summary"] do %> + <%= @data["summary"] %> + <% end %> + + <%= if @activity.local do %> + + + <% else %> + + <% end %> + + <%= for tag <- @data["tag"] || [] do %> + + <% end %> + + <%= for attachment <- @data["attachment"] || [] do %> + + <% end %> + + <%= if @data["inReplyTo"] do %> + + <% end %> + + <%= for id <- @activity.recipients do %> + <%= if id == Pleroma.Constants.as_public() do %> + + <% else %> + <%= unless Regex.match?(~r/^#{Pleroma.Web.base_url()}.+followers$/, id) do %> + + <% end %> + <% end %> + <% end %> + + <%= for {emoji, file} <- @data["emoji"] || %{} do %> + + <% end %> + diff --git a/lib/pleroma/web/templates/feed/feed/_author.xml.eex b/lib/pleroma/web/templates/feed/feed/_author.xml.eex new file mode 100644 index 000000000..25cbffada --- /dev/null +++ b/lib/pleroma/web/templates/feed/feed/_author.xml.eex @@ -0,0 +1,17 @@ + + <%= @user.ap_id %> + http://activitystrea.ms/schema/1.0/person + <%= @user.ap_id %> + <%= @user.nickname %> + <%= @user.name %> + <%= escape(@user.bio) %> + <%= escape(@user.bio) %> + <%= @user.nickname %> + + <%= if User.banner_url(@user) do %> + + <% end %> + <%= if @user.local do %> + true + <% end %> + diff --git a/lib/pleroma/web/templates/feed/feed/feed.xml.eex b/lib/pleroma/web/templates/feed/feed/feed.xml.eex new file mode 100644 index 000000000..fbfdc46b5 --- /dev/null +++ b/lib/pleroma/web/templates/feed/feed/feed.xml.eex @@ -0,0 +1,26 @@ + + + + <%= feed_url(@conn, :feed, @user.nickname) <> ".atom" %> + <%= @user.nickname <> "'s timeline" %> + <%= most_recent_update(@activities, @user) %> + <%= logo(@user) %> + + + + + <%= render @view_module, "_author.xml", assigns %> + + <%= if last_activity(@activities) do %> + + <% end %> + + <%= for activity <- @activities do %> + <%= render @view_module, "_activity.xml", Map.merge(assigns, %{activity: activity, data: activity_object_data(activity)}) %> + <% end %> + diff --git a/test/emails/admin_email_test.exs b/test/emails/admin_email_test.exs index 02c277a33..ad89f9213 100644 --- a/test/emails/admin_email_test.exs +++ b/test/emails/admin_email_test.exs @@ -19,8 +19,8 @@ test "build report email" do AdminEmail.report(to_user, reporter, account, [%{name: "Test", id: "12"}], "Test comment") status_url = Helpers.o_status_url(Pleroma.Web.Endpoint, :notice, "12") - reporter_url = Helpers.o_status_url(Pleroma.Web.Endpoint, :feed_redirect, reporter.id) - account_url = Helpers.o_status_url(Pleroma.Web.Endpoint, :feed_redirect, account.id) + reporter_url = Helpers.feed_url(Pleroma.Web.Endpoint, :feed_redirect, reporter.id) + account_url = Helpers.feed_url(Pleroma.Web.Endpoint, :feed_redirect, account.id) assert res.to == [{to_user.name, to_user.email}] assert res.from == {config[:name], config[:notify_email]} diff --git a/test/user_test.exs b/test/user_test.exs index ae21286e4..019e7b400 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -515,7 +515,7 @@ test "returns an ap_id for a user" do user = insert(:user) assert User.ap_id(user) == - Pleroma.Web.Router.Helpers.o_status_url( + Pleroma.Web.Router.Helpers.feed_url( Pleroma.Web.Endpoint, :feed_redirect, user.nickname @@ -526,7 +526,7 @@ test "returns an ap_followers link for a user" do user = insert(:user) assert User.ap_followers(user) == - Pleroma.Web.Router.Helpers.o_status_url( + Pleroma.Web.Router.Helpers.feed_url( Pleroma.Web.Endpoint, :feed_redirect, user.nickname diff --git a/test/web/feed/feed_controller_test.exs b/test/web/feed/feed_controller_test.exs new file mode 100644 index 000000000..1f44eae20 --- /dev/null +++ b/test/web/feed/feed_controller_test.exs @@ -0,0 +1,227 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Feed.FeedControllerTest do + use Pleroma.Web.ConnCase + + import Pleroma.Factory + + alias Pleroma.Object + alias Pleroma.User + + test "gets a feed", %{conn: conn} do + activity = insert(:note_activity) + + note = + insert(:note, + data: %{ + "attachment" => [ + %{ + "url" => [%{"mediaType" => "image/png", "href" => "https://pleroma.gov/image.png"}] + } + ], + "inReplyTo" => activity.data["id"] + } + ) + + note_activity = insert(:note_activity, note: note) + object = Object.normalize(note_activity) + user = User.get_cached_by_ap_id(note_activity.data["actor"]) + + conn = + conn + |> put_req_header("content-type", "application/atom+xml") + |> get("/users/#{user.nickname}/feed.atom") + + assert response(conn, 200) =~ object.data["content"] + end + + test "returns 404 for a missing feed", %{conn: conn} do + conn = + conn + |> put_req_header("content-type", "application/atom+xml") + |> get("/users/nonexisting/feed.atom") + + assert response(conn, 404) + end + + describe "feed_redirect" do + test "undefined format. it redirects to feed", %{conn: conn} do + note_activity = insert(:note_activity) + user = User.get_cached_by_ap_id(note_activity.data["actor"]) + + response = + conn + |> put_req_header("accept", "application/xml") + |> get("/users/#{user.nickname}") + |> response(302) + + assert response == + "You are being redirected." + end + + test "undefined format. it returns error when user not found", %{conn: conn} do + response = + conn + |> put_req_header("accept", "application/xml") + |> get("/users/jimm") + |> response(404) + + assert response == ~S({"error":"Not found"}) + end + + test "activity+json format. it redirects on actual feed of user", %{conn: conn} do + note_activity = insert(:note_activity) + user = User.get_cached_by_ap_id(note_activity.data["actor"]) + + response = + conn + |> put_req_header("accept", "application/activity+json") + |> get("/users/#{user.nickname}") + |> json_response(200) + + assert response["endpoints"] == %{ + "oauthAuthorizationEndpoint" => "#{Pleroma.Web.base_url()}/oauth/authorize", + "oauthRegistrationEndpoint" => "#{Pleroma.Web.base_url()}/api/v1/apps", + "oauthTokenEndpoint" => "#{Pleroma.Web.base_url()}/oauth/token", + "sharedInbox" => "#{Pleroma.Web.base_url()}/inbox", + "uploadMedia" => "#{Pleroma.Web.base_url()}/api/ap/upload_media" + } + + assert response["@context"] == [ + "https://www.w3.org/ns/activitystreams", + "http://localhost:4001/schemas/litepub-0.1.jsonld", + %{"@language" => "und"} + ] + + assert Map.take(response, [ + "followers", + "following", + "id", + "inbox", + "manuallyApprovesFollowers", + "name", + "outbox", + "preferredUsername", + "summary", + "tag", + "type", + "url" + ]) == %{ + "followers" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/followers", + "following" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/following", + "id" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}", + "inbox" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/inbox", + "manuallyApprovesFollowers" => false, + "name" => user.name, + "outbox" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/outbox", + "preferredUsername" => user.nickname, + "summary" => user.bio, + "tag" => [], + "type" => "Person", + "url" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}" + } + end + + test "activity+json format. it returns error whe use not found", %{conn: conn} do + response = + conn + |> put_req_header("accept", "application/activity+json") + |> get("/users/jimm") + |> json_response(404) + + assert response == "Not found" + end + + test "json format. it redirects on actual feed of user", %{conn: conn} do + note_activity = insert(:note_activity) + user = User.get_cached_by_ap_id(note_activity.data["actor"]) + + response = + conn + |> put_req_header("accept", "application/json") + |> get("/users/#{user.nickname}") + |> json_response(200) + + assert response["endpoints"] == %{ + "oauthAuthorizationEndpoint" => "#{Pleroma.Web.base_url()}/oauth/authorize", + "oauthRegistrationEndpoint" => "#{Pleroma.Web.base_url()}/api/v1/apps", + "oauthTokenEndpoint" => "#{Pleroma.Web.base_url()}/oauth/token", + "sharedInbox" => "#{Pleroma.Web.base_url()}/inbox", + "uploadMedia" => "#{Pleroma.Web.base_url()}/api/ap/upload_media" + } + + assert response["@context"] == [ + "https://www.w3.org/ns/activitystreams", + "http://localhost:4001/schemas/litepub-0.1.jsonld", + %{"@language" => "und"} + ] + + assert Map.take(response, [ + "followers", + "following", + "id", + "inbox", + "manuallyApprovesFollowers", + "name", + "outbox", + "preferredUsername", + "summary", + "tag", + "type", + "url" + ]) == %{ + "followers" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/followers", + "following" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/following", + "id" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}", + "inbox" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/inbox", + "manuallyApprovesFollowers" => false, + "name" => user.name, + "outbox" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/outbox", + "preferredUsername" => user.nickname, + "summary" => user.bio, + "tag" => [], + "type" => "Person", + "url" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}" + } + end + + test "json format. it returns error whe use not found", %{conn: conn} do + response = + conn + |> put_req_header("accept", "application/json") + |> get("/users/jimm") + |> json_response(404) + + assert response == "Not found" + end + + test "html format. it redirects on actual feed of user", %{conn: conn} do + note_activity = insert(:note_activity) + user = User.get_cached_by_ap_id(note_activity.data["actor"]) + + response = + conn + |> get("/users/#{user.nickname}") + |> response(200) + + assert response == + Fallback.RedirectController.redirector_with_meta( + conn, + %{user: user} + ).resp_body + end + + test "html format. it returns error when user not found", %{conn: conn} do + response = + conn + |> get("/users/jimm") + |> json_response(404) + + assert response == %{"error" => "Not found"} + end + end +end diff --git a/test/web/metadata/feed_test.exs b/test/web/metadata/feed_test.exs new file mode 100644 index 000000000..50e9ce52e --- /dev/null +++ b/test/web/metadata/feed_test.exs @@ -0,0 +1,18 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Metadata.Providers.FeedTest do + use Pleroma.DataCase + import Pleroma.Factory + alias Pleroma.Web.Metadata.Providers.Feed + + test "it renders a link to user's atom feed" do + user = insert(:user, nickname: "lain") + + assert Feed.build_tags(%{user: user}) == [ + {:link, + [rel: "alternate", type: "application/atom+xml", href: "/users/lain/feed.atom"], []} + ] + end +end diff --git a/test/web/ostatus/ostatus_controller_test.exs b/test/web/ostatus/ostatus_controller_test.exs index f06023dff..b1af918d8 100644 --- a/test/web/ostatus/ostatus_controller_test.exs +++ b/test/web/ostatus/ostatus_controller_test.exs @@ -72,28 +72,6 @@ test "decodes a salmon with a changed magic key", %{conn: conn} do end end - test "gets a feed", %{conn: conn} do - note_activity = insert(:note_activity) - object = Object.normalize(note_activity) - user = User.get_cached_by_ap_id(note_activity.data["actor"]) - - conn = - conn - |> put_req_header("content-type", "application/atom+xml") - |> get("/users/#{user.nickname}/feed.atom") - - assert response(conn, 200) =~ object.data["content"] - end - - test "returns 404 for a missing feed", %{conn: conn} do - conn = - conn - |> put_req_header("content-type", "application/atom+xml") - |> get("/users/nonexisting/feed.atom") - - assert response(conn, 404) - end - describe "GET object/2" do test "gets an object", %{conn: conn} do note_activity = insert(:note_activity) @@ -355,185 +333,6 @@ test "404s a nonexisting notice", %{conn: conn} do end end - describe "feed_redirect" do - test "undefined format. it redirects to feed", %{conn: conn} do - note_activity = insert(:note_activity) - user = User.get_cached_by_ap_id(note_activity.data["actor"]) - - response = - conn - |> put_req_header("accept", "application/xml") - |> get("/users/#{user.nickname}") - |> response(302) - - assert response == - "You are being redirected." - end - - test "undefined format. it returns error when user not found", %{conn: conn} do - response = - conn - |> put_req_header("accept", "application/xml") - |> get("/users/jimm") - |> response(404) - - assert response == ~S({"error":"Not found"}) - end - - test "activity+json format. it redirects on actual feed of user", %{conn: conn} do - note_activity = insert(:note_activity) - user = User.get_cached_by_ap_id(note_activity.data["actor"]) - - response = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/users/#{user.nickname}") - |> json_response(200) - - assert response["endpoints"] == %{ - "oauthAuthorizationEndpoint" => "#{Pleroma.Web.base_url()}/oauth/authorize", - "oauthRegistrationEndpoint" => "#{Pleroma.Web.base_url()}/api/v1/apps", - "oauthTokenEndpoint" => "#{Pleroma.Web.base_url()}/oauth/token", - "sharedInbox" => "#{Pleroma.Web.base_url()}/inbox", - "uploadMedia" => "#{Pleroma.Web.base_url()}/api/ap/upload_media" - } - - assert response["@context"] == [ - "https://www.w3.org/ns/activitystreams", - "http://localhost:4001/schemas/litepub-0.1.jsonld", - %{"@language" => "und"} - ] - - assert Map.take(response, [ - "followers", - "following", - "id", - "inbox", - "manuallyApprovesFollowers", - "name", - "outbox", - "preferredUsername", - "summary", - "tag", - "type", - "url" - ]) == %{ - "followers" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/followers", - "following" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/following", - "id" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}", - "inbox" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/inbox", - "manuallyApprovesFollowers" => false, - "name" => user.name, - "outbox" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/outbox", - "preferredUsername" => user.nickname, - "summary" => user.bio, - "tag" => [], - "type" => "Person", - "url" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}" - } - end - - test "activity+json format. it returns error whe use not found", %{conn: conn} do - response = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/users/jimm") - |> json_response(404) - - assert response == "Not found" - end - - test "json format. it redirects on actual feed of user", %{conn: conn} do - note_activity = insert(:note_activity) - user = User.get_cached_by_ap_id(note_activity.data["actor"]) - - response = - conn - |> put_req_header("accept", "application/json") - |> get("/users/#{user.nickname}") - |> json_response(200) - - assert response["endpoints"] == %{ - "oauthAuthorizationEndpoint" => "#{Pleroma.Web.base_url()}/oauth/authorize", - "oauthRegistrationEndpoint" => "#{Pleroma.Web.base_url()}/api/v1/apps", - "oauthTokenEndpoint" => "#{Pleroma.Web.base_url()}/oauth/token", - "sharedInbox" => "#{Pleroma.Web.base_url()}/inbox", - "uploadMedia" => "#{Pleroma.Web.base_url()}/api/ap/upload_media" - } - - assert response["@context"] == [ - "https://www.w3.org/ns/activitystreams", - "http://localhost:4001/schemas/litepub-0.1.jsonld", - %{"@language" => "und"} - ] - - assert Map.take(response, [ - "followers", - "following", - "id", - "inbox", - "manuallyApprovesFollowers", - "name", - "outbox", - "preferredUsername", - "summary", - "tag", - "type", - "url" - ]) == %{ - "followers" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/followers", - "following" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/following", - "id" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}", - "inbox" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/inbox", - "manuallyApprovesFollowers" => false, - "name" => user.name, - "outbox" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/outbox", - "preferredUsername" => user.nickname, - "summary" => user.bio, - "tag" => [], - "type" => "Person", - "url" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}" - } - end - - test "json format. it returns error whe use not found", %{conn: conn} do - response = - conn - |> put_req_header("accept", "application/json") - |> get("/users/jimm") - |> json_response(404) - - assert response == "Not found" - end - - test "html format. it redirects on actual feed of user", %{conn: conn} do - note_activity = insert(:note_activity) - user = User.get_cached_by_ap_id(note_activity.data["actor"]) - - response = - conn - |> get("/users/#{user.nickname}") - |> response(200) - - assert response == - Fallback.RedirectController.redirector_with_meta( - conn, - %{user: user} - ).resp_body - end - - test "html format. it returns error when user not found", %{conn: conn} do - response = - conn - |> get("/users/jimm") - |> json_response(404) - - assert response == %{"error" => "Not found"} - end - end - describe "GET /notice/:id/embed_player" do test "render embed player", %{conn: conn} do note_activity = insert(:note_activity) From 9a2f71f4d507301c52f1cc8f2e27fefc0b4385e9 Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Mon, 7 Oct 2019 12:29:33 +0000 Subject: [PATCH 31/37] mastodon api: fix exclude_replies (closes #1287) --- CHANGELOG.md | 1 + lib/pleroma/web/activity_pub/activity_pub.ex | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f193ec1ff..8b24db7f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -85,6 +85,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - ActivityPub: Fix `/users/:nickname/inbox` crashing without an authenticated user - MRF: fix ability to follow a relay when AntiFollowbotPolicy was enabled - Mastodon API: Blocks are now treated consistently between the Streaming API and the Timeline APIs +- Mastodon API: `exclude_replies` is correctly handled again. ### Added - Expiring/ephemeral activites. All activities can have expires_at value set, which controls when they should be deleted automatically. diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 5052d1304..9f29087df 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -780,8 +780,8 @@ defp restrict_media(query, _), do: query defp restrict_replies(query, %{"exclude_replies" => val}) when val == "true" or val == "1" do from( - activity in query, - where: fragment("?->'object'->>'inReplyTo' is null", activity.data) + [_activity, object] in query, + where: fragment("?->>'inReplyTo' is null", object.data) ) end From a502155274454e6f0c1213786b2934e01284e8d7 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Mon, 7 Oct 2019 17:42:33 +0300 Subject: [PATCH 32/37] Bump pleroma-fe bundle to https://git.pleroma.social/pleroma/pleroma-fe/commit/122323f35c32a4f12a345a8b3f163e9318f5dea3 --- priv/static/index.html | 2 +- ...526ea.css => app.4e8e80a2f95232cff399.css} | 38 +++++++++++++----- .../css/app.4e8e80a2f95232cff399.css.map | 1 + .../css/app.cb3673e4b661fd9526ea.css.map | 1 - priv/static/static/font/config.json | 12 ++++++ .../static/static/font/css/fontello-codes.css | 2 + .../static/font/css/fontello-embedded.css | 14 ++++--- .../static/font/css/fontello-ie7-codes.css | 2 + priv/static/static/font/css/fontello-ie7.css | 2 + priv/static/static/font/css/fontello.css | 16 ++++---- priv/static/static/font/demo.html | 20 ++++----- priv/static/static/font/font/fontello.eot | Bin 19452 -> 20152 bytes priv/static/static/font/font/fontello.svg | 4 ++ priv/static/static/font/font/fontello.ttf | Bin 19284 -> 19984 bytes priv/static/static/font/font/fontello.woff | Bin 11776 -> 12248 bytes priv/static/static/font/font/fontello.woff2 | Bin 9980 -> 10392 bytes .../static/js/2.48f39bc510b7c0a7fca6.js | 2 + .../static/js/2.48f39bc510b7c0a7fca6.js.map | 1 + .../static/js/app.4ab7097a5650339b9e3d.js | 2 + .../static/js/app.4ab7097a5650339b9e3d.js.map | 1 + .../static/js/app.8098503330c7dd14a415.js | 2 - .../static/js/app.8098503330c7dd14a415.js.map | 1 - ...js => vendors~app.24e6ba2d196f6210feda.js} | 16 ++++---- .../vendors~app.24e6ba2d196f6210feda.js.map | 1 + .../vendors~app.4cedffe4993b111c7421.js.map | 1 - priv/static/sw-pleroma.js | 2 +- 26 files changed, 95 insertions(+), 48 deletions(-) rename priv/static/static/css/{app.cb3673e4b661fd9526ea.css => app.4e8e80a2f95232cff399.css} (82%) create mode 100644 priv/static/static/css/app.4e8e80a2f95232cff399.css.map delete mode 100644 priv/static/static/css/app.cb3673e4b661fd9526ea.css.map create mode 100644 priv/static/static/js/2.48f39bc510b7c0a7fca6.js create mode 100644 priv/static/static/js/2.48f39bc510b7c0a7fca6.js.map create mode 100644 priv/static/static/js/app.4ab7097a5650339b9e3d.js create mode 100644 priv/static/static/js/app.4ab7097a5650339b9e3d.js.map delete mode 100644 priv/static/static/js/app.8098503330c7dd14a415.js delete mode 100644 priv/static/static/js/app.8098503330c7dd14a415.js.map rename priv/static/static/js/{vendors~app.4cedffe4993b111c7421.js => vendors~app.24e6ba2d196f6210feda.js} (53%) create mode 100644 priv/static/static/js/vendors~app.24e6ba2d196f6210feda.js.map delete mode 100644 priv/static/static/js/vendors~app.4cedffe4993b111c7421.js.map diff --git a/priv/static/index.html b/priv/static/index.html index f681f4def..fc5eee901 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/css/app.cb3673e4b661fd9526ea.css b/priv/static/static/css/app.4e8e80a2f95232cff399.css similarity index 82% rename from priv/static/static/css/app.cb3673e4b661fd9526ea.css rename to priv/static/static/css/app.4e8e80a2f95232cff399.css index e083f12c8..ca3d4e41f 100644 --- a/priv/static/static/css/app.cb3673e4b661fd9526ea.css +++ b/priv/static/static/css/app.4e8e80a2f95232cff399.css @@ -1,6 +1,32 @@ +.with-load-more-footer { + padding: 10px; + text-align: center; + border-top: 1px solid; + border-top-color: #222; + border-top-color: var(--border, #222); +} +.with-load-more-footer .error { + font-size: 14px; +} +.tab-switcher { + display: -ms-flexbox; + display: flex; + -ms-flex-direction: column; + flex-direction: column; +} +.tab-switcher .contents { + -ms-flex: 1 0 auto; + flex: 1 0 auto; + min-height: 0px; +} .tab-switcher .contents .hidden { display: none; } +.tab-switcher .contents.scrollable-tabs { + -ms-flex-preferred-size: 0; + flex-basis: 0; + overflow-y: auto; +} .tab-switcher .tabs { display: -ms-flexbox; display: flex; @@ -65,16 +91,6 @@ .tab-switcher .tabs .tab-wrapper:not(.active)::after { border-bottom-color: #222; border-bottom-color: var(--border, #222); } -.with-load-more-footer { - padding: 10px; - text-align: center; - border-top: 1px solid; - border-top-color: #222; - border-top-color: var(--border, #222); -} -.with-load-more-footer .error { - font-size: 14px; -} .with-subscription-loading { padding: 10px; text-align: center; @@ -83,4 +99,4 @@ .with-subscription-loading .error { font-size: 14px; } -/*# sourceMappingURL=app.cb3673e4b661fd9526ea.css.map*/ \ No newline at end of file +/*# sourceMappingURL=app.4e8e80a2f95232cff399.css.map*/ \ No newline at end of file diff --git a/priv/static/static/css/app.4e8e80a2f95232cff399.css.map b/priv/static/static/css/app.4e8e80a2f95232cff399.css.map new file mode 100644 index 000000000..dc2c92ced --- /dev/null +++ b/priv/static/static/css/app.4e8e80a2f95232cff399.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack:///./src/hocs/with_load_more/with_load_more.scss","webpack:///./src/components/tab_switcher/tab_switcher.scss","webpack:///./src/hocs/with_subscription/with_subscription.scss"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,C;ACTA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,C;AClFA;AACA;AACA;AACA;AACA;AACA;AACA,C","file":"static/css/app.4e8e80a2f95232cff399.css","sourcesContent":[".with-load-more-footer {\n padding: 10px;\n text-align: center;\n border-top: 1px solid;\n border-top-color: #222;\n border-top-color: var(--border, #222);\n}\n.with-load-more-footer .error {\n font-size: 14px;\n}",".tab-switcher {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-direction: column;\n flex-direction: column;\n}\n.tab-switcher .contents {\n -ms-flex: 1 0 auto;\n flex: 1 0 auto;\n min-height: 0px;\n}\n.tab-switcher .contents .hidden {\n display: none;\n}\n.tab-switcher .contents.scrollable-tabs {\n -ms-flex-preferred-size: 0;\n flex-basis: 0;\n overflow-y: auto;\n}\n.tab-switcher .tabs {\n display: -ms-flexbox;\n display: flex;\n position: relative;\n width: 100%;\n overflow-y: hidden;\n overflow-x: auto;\n padding-top: 5px;\n box-sizing: border-box;\n}\n.tab-switcher .tabs::after, .tab-switcher .tabs::before {\n display: block;\n content: \"\";\n -ms-flex: 1 1 auto;\n flex: 1 1 auto;\n border-bottom: 1px solid;\n border-bottom-color: #222;\n border-bottom-color: var(--border, #222);\n}\n.tab-switcher .tabs .tab-wrapper {\n height: 28px;\n position: relative;\n display: -ms-flexbox;\n display: flex;\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n}\n.tab-switcher .tabs .tab-wrapper .tab {\n width: 100%;\n min-width: 1px;\n position: relative;\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n padding: 6px 1em;\n padding-bottom: 99px;\n margin-bottom: -93px;\n white-space: nowrap;\n}\n.tab-switcher .tabs .tab-wrapper .tab:not(.active) {\n z-index: 4;\n}\n.tab-switcher .tabs .tab-wrapper .tab:not(.active):hover {\n z-index: 6;\n}\n.tab-switcher .tabs .tab-wrapper .tab.active {\n background: transparent;\n z-index: 5;\n}\n.tab-switcher .tabs .tab-wrapper .tab img {\n max-height: 26px;\n vertical-align: top;\n margin-top: -5px;\n}\n.tab-switcher .tabs .tab-wrapper:not(.active)::after {\n content: \"\";\n position: absolute;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 7;\n border-bottom: 1px solid;\n border-bottom-color: #222;\n border-bottom-color: var(--border, #222);\n}",".with-subscription-loading {\n padding: 10px;\n text-align: center;\n}\n.with-subscription-loading .error {\n font-size: 14px;\n}"],"sourceRoot":""} \ No newline at end of file diff --git a/priv/static/static/css/app.cb3673e4b661fd9526ea.css.map b/priv/static/static/css/app.cb3673e4b661fd9526ea.css.map deleted file mode 100644 index 8cecb0901..000000000 --- a/priv/static/static/css/app.cb3673e4b661fd9526ea.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["webpack:///./src/components/tab_switcher/tab_switcher.scss","webpack:///./src/hocs/with_load_more/with_load_more.scss","webpack:///./src/hocs/with_subscription/with_subscription.scss"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,C;AClEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,C;ACTA;AACA;AACA;AACA;AACA;AACA;AACA,C","file":"static/css/app.cb3673e4b661fd9526ea.css","sourcesContent":[".tab-switcher .contents .hidden {\n display: none;\n}\n.tab-switcher .tabs {\n display: -ms-flexbox;\n display: flex;\n position: relative;\n width: 100%;\n overflow-y: hidden;\n overflow-x: auto;\n padding-top: 5px;\n box-sizing: border-box;\n}\n.tab-switcher .tabs::after, .tab-switcher .tabs::before {\n display: block;\n content: \"\";\n -ms-flex: 1 1 auto;\n flex: 1 1 auto;\n border-bottom: 1px solid;\n border-bottom-color: #222;\n border-bottom-color: var(--border, #222);\n}\n.tab-switcher .tabs .tab-wrapper {\n height: 28px;\n position: relative;\n display: -ms-flexbox;\n display: flex;\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n}\n.tab-switcher .tabs .tab-wrapper .tab {\n width: 100%;\n min-width: 1px;\n position: relative;\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n padding: 6px 1em;\n padding-bottom: 99px;\n margin-bottom: -93px;\n white-space: nowrap;\n}\n.tab-switcher .tabs .tab-wrapper .tab:not(.active) {\n z-index: 4;\n}\n.tab-switcher .tabs .tab-wrapper .tab:not(.active):hover {\n z-index: 6;\n}\n.tab-switcher .tabs .tab-wrapper .tab.active {\n background: transparent;\n z-index: 5;\n}\n.tab-switcher .tabs .tab-wrapper .tab img {\n max-height: 26px;\n vertical-align: top;\n margin-top: -5px;\n}\n.tab-switcher .tabs .tab-wrapper:not(.active)::after {\n content: \"\";\n position: absolute;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 7;\n border-bottom: 1px solid;\n border-bottom-color: #222;\n border-bottom-color: var(--border, #222);\n}",".with-load-more-footer {\n padding: 10px;\n text-align: center;\n border-top: 1px solid;\n border-top-color: #222;\n border-top-color: var(--border, #222);\n}\n.with-load-more-footer .error {\n font-size: 14px;\n}",".with-subscription-loading {\n padding: 10px;\n text-align: center;\n}\n.with-subscription-loading .error {\n font-size: 14px;\n}"],"sourceRoot":""} \ No newline at end of file diff --git a/priv/static/static/font/config.json b/priv/static/static/font/config.json index 72a48a74f..c0cf17271 100755 --- a/priv/static/static/font/config.json +++ b/priv/static/static/font/config.json @@ -240,6 +240,12 @@ "code": 59419, "src": "fontawesome" }, + { + "uid": "d862a10e1448589215be19702f98f2c1", + "css": "smile", + "code": 61720, + "src": "fontawesome" + }, { "uid": "671f29fa10dda08074a4c6a341bb4f39", "css": "bell-alt", @@ -291,6 +297,12 @@ "css": "zoom-in", "code": 59420, "src": "fontawesome" + }, + { + "uid": "0bda4bc779d4c32623dec2e43bd67ee8", + "css": "gauge", + "code": 61668, + "src": "fontawesome" } ] } \ No newline at end of file diff --git a/priv/static/static/font/css/fontello-codes.css b/priv/static/static/font/css/fontello-codes.css index 2083f618a..87b4930e5 100755 --- a/priv/static/static/font/css/fontello-codes.css +++ b/priv/static/static/font/css/fontello-codes.css @@ -34,10 +34,12 @@ .icon-link-ext:before { content: '\f08e'; } /* '' */ .icon-link-ext-alt:before { content: '\f08f'; } /* '' */ .icon-menu:before { content: '\f0c9'; } /* '' */ .icon-mail-alt:before { content: '\f0e0'; } /* '' */ +.icon-gauge:before { content: '\f0e4'; } /* '' */ .icon-comment-empty:before { content: '\f0e5'; } /* '' */ .icon-bell-alt:before { content: '\f0f3'; } /* '' */ .icon-plus-squared:before { content: '\f0fe'; } /* '' */ .icon-reply:before { content: '\f112'; } /* '' */ +.icon-smile:before { content: '\f118'; } /* '' */ .icon-lock-open-alt:before { content: '\f13e'; } /* '' */ .icon-ellipsis:before { content: '\f141'; } /* '' */ .icon-play-circled:before { content: '\f144'; } /* '' */ diff --git a/priv/static/static/font/css/fontello-embedded.css b/priv/static/static/font/css/fontello-embedded.css index ad4246e6e..861ef86e0 100755 --- a/priv/static/static/font/css/fontello-embedded.css +++ b/priv/static/static/font/css/fontello-embedded.css @@ -1,15 +1,15 @@ @font-face { font-family: 'fontello'; - src: url('../font/fontello.eot?49712213'); - src: url('../font/fontello.eot?49712213#iefix') format('embedded-opentype'), - url('../font/fontello.svg?49712213#fontello') format('svg'); + src: url('../font/fontello.eot?899037'); + src: url('../font/fontello.eot?899037#iefix') format('embedded-opentype'), + url('../font/fontello.svg?899037#fontello') format('svg'); font-weight: normal; font-style: normal; } @font-face { font-family: 'fontello'; - src: url('data:application/octet-stream;base64,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') format('woff'), - url('data:application/octet-stream;base64,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') format('truetype'); + src: url('data:application/octet-stream;base64,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') format('woff'), + url('data:application/octet-stream;base64,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') format('truetype'); } /* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ /* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ @@ -17,7 +17,7 @@ @font-face { @media screen and (-webkit-min-device-pixel-ratio:0) { @font-face { font-family: 'fontello'; - src: url('../font/fontello.svg?49712213#fontello') format('svg'); + src: url('../font/fontello.svg?899037#fontello') format('svg'); } } */ @@ -87,10 +87,12 @@ .icon-link-ext:before { content: '\f08e'; } /* '' */ .icon-link-ext-alt:before { content: '\f08f'; } /* '' */ .icon-menu:before { content: '\f0c9'; } /* '' */ .icon-mail-alt:before { content: '\f0e0'; } /* '' */ +.icon-gauge:before { content: '\f0e4'; } /* '' */ .icon-comment-empty:before { content: '\f0e5'; } /* '' */ .icon-bell-alt:before { content: '\f0f3'; } /* '' */ .icon-plus-squared:before { content: '\f0fe'; } /* '' */ .icon-reply:before { content: '\f112'; } /* '' */ +.icon-smile:before { content: '\f118'; } /* '' */ .icon-lock-open-alt:before { content: '\f13e'; } /* '' */ .icon-ellipsis:before { content: '\f141'; } /* '' */ .icon-play-circled:before { content: '\f144'; } /* '' */ diff --git a/priv/static/static/font/css/fontello-ie7-codes.css b/priv/static/static/font/css/fontello-ie7-codes.css index cbc410004..11c8c10a2 100755 --- a/priv/static/static/font/css/fontello-ie7-codes.css +++ b/priv/static/static/font/css/fontello-ie7-codes.css @@ -34,10 +34,12 @@ .icon-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerH .icon-link-ext-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-menu { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-mail-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-gauge { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-comment-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-bell-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-plus-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-reply { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-smile { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-lock-open-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-ellipsis { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-play-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } diff --git a/priv/static/static/font/css/fontello-ie7.css b/priv/static/static/font/css/fontello-ie7.css index 1ef174bf8..edf83afe6 100755 --- a/priv/static/static/font/css/fontello-ie7.css +++ b/priv/static/static/font/css/fontello-ie7.css @@ -45,10 +45,12 @@ .icon-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerH .icon-link-ext-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-menu { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-mail-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-gauge { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-comment-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-bell-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-plus-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-reply { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-smile { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-lock-open-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-ellipsis { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-play-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } diff --git a/priv/static/static/font/css/fontello.css b/priv/static/static/font/css/fontello.css index 84fd6802c..a6b3c9193 100755 --- a/priv/static/static/font/css/fontello.css +++ b/priv/static/static/font/css/fontello.css @@ -1,11 +1,11 @@ @font-face { font-family: 'fontello'; - src: url('../font/fontello.eot?4060331'); - src: url('../font/fontello.eot?4060331#iefix') format('embedded-opentype'), - url('../font/fontello.woff2?4060331') format('woff2'), - url('../font/fontello.woff?4060331') format('woff'), - url('../font/fontello.ttf?4060331') format('truetype'), - url('../font/fontello.svg?4060331#fontello') format('svg'); + src: url('../font/fontello.eot?70867224'); + src: url('../font/fontello.eot?70867224#iefix') format('embedded-opentype'), + url('../font/fontello.woff2?70867224') format('woff2'), + url('../font/fontello.woff?70867224') format('woff'), + url('../font/fontello.ttf?70867224') format('truetype'), + url('../font/fontello.svg?70867224#fontello') format('svg'); font-weight: normal; font-style: normal; } @@ -15,7 +15,7 @@ @font-face { @media screen and (-webkit-min-device-pixel-ratio:0) { @font-face { font-family: 'fontello'; - src: url('../font/fontello.svg?4060331#fontello') format('svg'); + src: url('../font/fontello.svg?70867224#fontello') format('svg'); } } */ @@ -90,10 +90,12 @@ .icon-link-ext:before { content: '\f08e'; } /* '' */ .icon-link-ext-alt:before { content: '\f08f'; } /* '' */ .icon-menu:before { content: '\f0c9'; } /* '' */ .icon-mail-alt:before { content: '\f0e0'; } /* '' */ +.icon-gauge:before { content: '\f0e4'; } /* '' */ .icon-comment-empty:before { content: '\f0e5'; } /* '' */ .icon-bell-alt:before { content: '\f0f3'; } /* '' */ .icon-plus-squared:before { content: '\f0fe'; } /* '' */ .icon-reply:before { content: '\f112'; } /* '' */ +.icon-smile:before { content: '\f118'; } /* '' */ .icon-lock-open-alt:before { content: '\f13e'; } /* '' */ .icon-ellipsis:before { content: '\f141'; } /* '' */ .icon-play-circled:before { content: '\f144'; } /* '' */ diff --git a/priv/static/static/font/demo.html b/priv/static/static/font/demo.html index 225e4ec5b..afae72fa5 100755 --- a/priv/static/static/font/demo.html +++ b/priv/static/static/font/demo.html @@ -229,11 +229,11 @@ body { } @font-face { font-family: 'fontello'; - src: url('./font/fontello.eot?25455785'); - src: url('./font/fontello.eot?25455785#iefix') format('embedded-opentype'), - url('./font/fontello.woff?25455785') format('woff'), - url('./font/fontello.ttf?25455785') format('truetype'), - url('./font/fontello.svg?25455785#fontello') format('svg'); + src: url('./font/fontello.eot?56851497'); + src: url('./font/fontello.eot?56851497#iefix') format('embedded-opentype'), + url('./font/fontello.woff?56851497') format('woff'), + url('./font/fontello.ttf?56851497') format('truetype'), + url('./font/fontello.svg?56851497#fontello') format('svg'); font-weight: normal; font-style: normal; } @@ -349,21 +349,23 @@ body {
icon-link-ext-alt0xf08f
icon-menu0xf0c9
icon-mail-alt0xf0e0
-
icon-comment-empty0xf0e5
+
icon-gauge0xf0e4
+
icon-comment-empty0xf0e5
icon-bell-alt0xf0f3
icon-plus-squared0xf0fe
icon-reply0xf112
-
icon-lock-open-alt0xf13e
+
icon-smile0xf118
+
icon-lock-open-alt0xf13e
icon-ellipsis0xf141
icon-play-circled0xf144
-
icon-thumbs-up-alt0xf164
-
icon-binoculars0xf1e5
+
icon-thumbs-up-alt0xf164
+
icon-binoculars0xf1e5
icon-user-plus0xf234
diff --git a/priv/static/static/font/font/fontello.eot b/priv/static/static/font/font/fontello.eot index d08692e84134f5c16e188e97c841e691f820e15a..1703fd97fce7f856264563cd89df72928771f153 100755 GIT binary patch delta 1320 zcmZWpTTB~A6umRE-t_|v7-JjDxW<5?wqsmk)`lAM2oWOXr4&`9N*&C*0uu|IDrurx ziQI}tX#(*_Q>m%)QS~iVihryMsf`*%NkxC62B{xS`3RNOe$*yu#7AQHE;jw?j?SKY z=H9zAIy2g5AClB{!l#A+%7@l?k-NVebgXqTdw#3ji)U`Bv1z=W|a0x_1G< zG835%UtC)~2GC;wP(@}#%MqL}$9x7^KD%&c<`sJJC;)E)XgxYN9h%Z*@7x4Koc?^fCP9EIZy!Dk-G~32Qn@v0i4KF1;7DhTuXu)TmUWJZrhqMs;9R2n zuQpb0G%;VTC|5bhj+82lH_}gKn_SueBIXn_yzq=kE*)-et zYKg8GWFDxEd`bBO=B#xL7+j9pu7>@R^TlAe$Pd@fRaBh4I+I!%8d^yugU!FQm@NMs zjXuFNPzfeHb`cIiPgiG+mQqIgK!H{&B<~nUC}DJ5y1xR!8z#u*!YBxWd<^7*e4x8a zuh*f0)zuoMtj2*(xk#feKy-RDX%W2vbg@CpTWtZVlOel^l=8S@)T*Xt7GPejnpj;l zKw8X%o~eplUea=ElaW&o1do()CS!T-kD`slQZ(y>_TYP$SC0FsZ#;hW!i3nrbSHgx zk$-#r$#?G%a%hx}99A0@CFM?^&snBc8n=r!n2P*$e|)^!CpE`|e$F#8g?xIJP~c5 zuvBTPKGg+uU+F=AN&nO^WLT{ht8>OLu^l}GcXFp)#jrouahxM=@>25kKndYxv!PS7 V(}|f*J1<)fzqv4-Td=ntek)BhT7VP}fkb!~q2LprAwT#rn z6fy4=B@7G}7l86+89)K{lPsJJ43-=~zDh=JNyXCI{||wD9Uy;4PJXgyqK=R(kZt({ zNW|qPRunKc^DbjxuyO(N74i~uQ_swmR|N7?7#Nt&7UUO~OrIjLlYzk&=m5sQ1x2X^ zPaU=QFfe$XnY@7Uu(b*UBTyd$qZ|V>n56@xxqv!V7+4rS0VR|e6o4$I7cwu5UYLC7 z|8VldgAY$XeE#t7qtN69OtS1S_Aw@XMMj|NC@_p+zzQczF~9wh!l;(#PD#ejoXZ}U7`Zf2mKRSq$mxg2>IC(Af#ZT4}>;Q;`of2-{P diff --git a/priv/static/static/font/font/fontello.svg b/priv/static/static/font/font/fontello.svg index fdd7caa73..f5e497ce4 100755 --- a/priv/static/static/font/font/fontello.svg +++ b/priv/static/static/font/font/fontello.svg @@ -76,6 +76,8 @@ + + @@ -84,6 +86,8 @@ + + diff --git a/priv/static/static/font/font/fontello.ttf b/priv/static/static/font/font/fontello.ttf index 6f5a81d7670e06dbc3cac29ea406c81af010f651..e9ed780311d58bedd808d37acfe47495fbb9ba0a 100755 GIT binary patch delta 1328 zcmZWpZA@EL7=F(=xA*po7D{PL)78>J!L}DUWW^;>; z3mRM|gVh*K^q=uNF`=>`7unRKWrH;{McIGON)Q($#b9gJ?DAP zd-~jy`}5mm`8~4ikN>^(+;0HnV*oBT_EOBj=d!y1t$P6An2b(EuBD>@>zQ21ORUXs6R115gs>XZmj}5BkROzoDlAC8q9HjsGN?(&VT+* z?jGhFn14HacC0hpu@Q0rG+o7s(Mb4wl)P5-A?9)e$C+?sV&nZMDgc_XDg7aOc0RVb z_Qpkkqg$94Mdv1>+pV5;fS$#bz3;~SK(Gvm8`ys2MOLHG4;*B1p%NSzaVcA>E)_^$ zOW#RvrMJ@$(%Fn5W64OF)=Yb5JhQzM$mIZ1u&-L$SKU8QC9Jfz3R~b_L;GVGyc%yv>I1|@W{%E&ui5;ZB1eMDXg_~O*5qjn~-pixJhJ}Qwq zJE0fKqDyl|PG_@ny6)jFJ!i8PXa6YJNE`*T-fSFxeQ9y1iH1fNu3sJ%JLhhtZlB>_ zUf=!d6+(^;(Ej5(t4dq!4TZdVo!Yuxu)$W~&pH=I%0qJT!f+E8n7q_;?q)1@^H2E! zkbJAIows?0_kKF9Z~6x`EQMDXep$V7`G6CbWct($=Bwt1mR`$pxmcdHeuCxbA^4MBjT(maC!2>j;wMifSDUqjS4@S^O-&@e SmfXBzKJvosL^37KYX1e5FfgzLB$t&au>Jq{g@M7|hJk@WA*~=iw`<~rNFYCk zfq}aqJ+Zg|C@uixn*eE!^qk7HVCSEP3=FJ47#NJMWuzvihIib%bPrY|AG=A}%+vqJXiPcNqhNl?#xs zke8U7dSZrYkfx+v{#2<&PRTvn7 z+87w+7?{B<9U#pG)TqM1!te3Y5vv{~(w_je+5#5Ck)*18F-5X3zlAju6bC38aDH0m2MgKpGepAk3f* zq=9CGFayxqPrxt%VFq20=l_3Wy28}ES&ePG#%4!@JItF+O`X|fHVX4iis!fa%D~P1 z0;rPVX8fjP7(IE4wK=2SS>sb>HH diff --git a/priv/static/static/font/font/fontello.woff b/priv/static/static/font/font/fontello.woff index 79972a576ca7c7008a389c3894720f6fdb2d27f1..1d5025d3cccf300b56cdcea3fb71b091fdbf3bdb 100755 GIT binary patch delta 10117 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 F!2S z<8ePVB@Miuhu+BWcQX9%aDa5+@|#b$ebXynaw}gZo=PC#2G=zQ8~pp%q!C^6H34Mx zWEp#sS)4HcWqd;}Xdw%$*dU-oAM4`io!mxRH1d$QXb-DOw2j)s&b_!l9l48rex@cV z;v#8I-I?`(xGA?xA?HZRTw-xL?gj_DgvhOtdLXMPVEn&F^x!ZbGFGKBnqFtQ8 z(3WoZpO(ODX7NMFiNW)GBhA1}Un+T&mbEL)9A)#U~842RuT$i}5NrJ=q zOTnTZQ!bwxnZF^ew-qjnd6dH`}PaeB=f=oh6kl+PkueJ8CShMwGSDa812kPW=*+@5kb^V zm0^ap6U{7wk@9{reNRd>3#(*K*Ezw*9Z4fWk+bEYpB%oh-E)d-mt1}s1Vx%KMlj8@ zOz2n;NF;|SqR}M4;^g-cBN9h%)8XV&GdsSWMSBKAoy2XSSNXVLZ@;gNozu9|^OQD| z(!qkVj^OwbC(<6;cd8Oy)-VpEpH2$qJj)5Y(#XrnAtIt`-~)ql($M7#|w{7?(k& zdHb}bhPHGB`oLNf{4x%kZLKSLhCyPDL?0}yZqv5~5B9}}Gd9kiTY7CNm%{1;?*+DY z@x7+4b+YXvm}_=Q*})=p(^lPj^fT94g*+14EFr}!n7@=j_|~^{Rw#BzUVWP6E`Jw$ zkVl<^DYSzCNq_cp?Xeb4Ic#BIb9Zau@2u5!h0#~G`=w(NV~6kfR=%S|D5zfD{`7OW z7jhov?FCdJ{_w9**Uvup6ZN&}?mPD1&;M;dcruptuxrxkQ)F zCo!9rhY$P){#bexgbw50&rjP#^%V2-3_M`X8?J)=+|Iqf{->40>vW4-?PNeo6qy&Y z?u{W?aaahuCy?B^sXrXUsalu?enAh8(@y~NdQ>@5qAYZco9eGnava~Qc>TJ`L4H^J zrJbqCN7$gxFeRS6a*tya9G0mDS@x8@Wgm4iJsD{Ozc zO|1o*oh55V;Uu8ld;)%2J}zC7j5w*uxjlz$4Z^3k-NbUlpY;UOI_9|2<**&U1;NUO8(;=0D>5>yZtIYq9rN*daqrWvsvZ==81Ecz| z)MBsTfPO!O03h$Faf+;l1%h?oIXSVsT8k^WU1i{onZG1ew|{A4|LB_|sKp#bo;VA? zw(>Oe0MpO$C|-+n9}TqIdn&xr!f&+;9)NiBKWx4q-9N1upF3ZZ-;LEdm#h6m;b;8? zhWNjoBSuCLJK#Q1yX!X|AVF(75JD2)e#k*Kbmp_?L&>DvNDVhDEtq0#WPDGx)Jt6xlEt%*CWpbbM7)XZ`5UdtFVTjOSF<1*+n;XsaxzE0s>@sMHvVJXk z?P}kj1hw!RdcZ#rd=S|*@HBYYqGG}51lf^nPTIlwv z)3-q)#^m`F-THPuCdEaZ&laXE;~-2Nd>bySnQhqSSkp-Wk zOEneyq&AR%pLOmBW9k&99z9jpK849`2X}(@p8!@@X3KBo=^?Y@wshxNNY~U|IN#1$7b$XD2uS&(68pap(-~s)8QHi|I{1p)(7xMFW>sqqjTMEU{Y2b* Uu5YCOfIQm(f1Qm)eKz0!16TTV7ytkO diff --git a/priv/static/static/font/font/fontello.woff2 b/priv/static/static/font/font/fontello.woff2 index 94d79274a136fa37c2e1a950ae2b30f593e85cd2..078991eb8f784ad2b420cc2367a5c5813055e053 100755 GIT binary patch literal 10392 zcmV;JC}-DqPew8T0RR9104SIM4*&oF08S7904P2H0RR9100000000000000000000 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 C)akbX literal 9980 zcmV4O$UR041oq4Awo5(*I?&v z2Pov?>n`iaG;qKte z&1UOD!8*ew!ryBf3%+TWG%CdvUfjDWsWTQ%>6lG#{9RSr+=KRcg;uyY?Hg%F+PM zX=lj=%S9kTa)B7M4iPM)LAoIYf5PE$g89eGMmpa|93CPhjI?((d3!A;%Hs-qw}9rZr}N00%4{~E330qzu7 znJNh=Q6eTbV-Nm)%gzA8|Kr+cK}evOo4y5_DbPYO%ajx|A6DoJ9j8vFY~^wZrM8>P zms{g@5DJA$0yski49*)E)0{GV(CTge``tY=XU?v)0uz%S=owYlGOqzud)Gj*BWFM% zeBdC)=m#lq_A39BdET6_(#BHDf)XXaL|`b;r)9{w*jM}YCJ1btaDj1?bTww`Za?xECXu1?-$|mWrEW^|5sr`0MPV|9LlhR}`CEBuPG7LCQz_mzN`s};I z+N#BvNVmcyi~@-o?t5Us+R}?CY6(JR2fimHXj*lOTqfN;0{UkG%|I~fASFG1mrb8# zJ_fXXt3m-#ZEaOEWd6I)QOLibuL1l%KmYU>hzf|T%w=2jEk1XE>VcPX*y=fu36}u- z`;&oGA(e#Q^Qp^|wMH5aXt6&`Q2%yN{{bAusO%q4sq%2fJbsJuc=dex1?$Cv7wQ+f z7q%DUf4OG_;B(r(QduS$_d1<}?A0~1*WUh*r&gVM4H`9R)}ockVsp4WzCfra(l;

+W&s!!ds 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 G6&MMca|N6L diff --git a/priv/static/static/js/2.48f39bc510b7c0a7fca6.js b/priv/static/static/js/2.48f39bc510b7c0a7fca6.js new file mode 100644 index 000000000..eabbcf690 --- /dev/null +++ b/priv/static/static/js/2.48f39bc510b7c0a7fca6.js @@ -0,0 +1,2 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[2],{987:function(t,e,i){"use strict";i.r(e);var n=i(988),c=i.n(n);for(var r in n)"default"!==r&&function(t){i.d(e,t,function(){return n[t]})}(r);var a=i(991),s=i(0);var o=function(t){i(989)},u=Object(s.a)(c.a,a.a,a.b,!1,o,null,null);e.default=u.exports},988:function(t,e,i){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=c(i(332));function c(t){return t&&t.__esModule?t:{default:t}}var r={components:{TabSwitcher:c(i(197)).default},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,c=this.$store;fetch(t).then(function(t){t.blob().then(function(t){var r=new File([t],e,{mimetype:"image/png"}),a=new FormData;a.append("file",r),n.default.uploadMedia({store:c,formData:a}).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")})})})}}};e.default=r},989:function(t,e,i){var n=i(990);"string"==typeof n&&(n=[[t.i,n,""]]),n.locals&&(t.exports=n.locals);(0,i(2).default)("cc6cdea4",n,!0,{})},990:function(t,e,i){(t.exports=i(1)(!1)).push([t.i,".sticker-picker{width:100%;position:relative}.sticker-picker .tab-switcher{position:absolute;top:0;bottom:0;left:0;right:0}.sticker-picker .sticker-picker-content .sticker{display:inline-block;width:20%;height:20%}.sticker-picker .sticker-picker-content .sticker img{width:100%}.sticker-picker .sticker-picker-content .sticker img:hover{filter:drop-shadow(0 0 5px var(--link,#d8a070))}",""])},991:function(t,e,i){"use strict";i.d(e,"a",function(){return n}),i.d(e,"b",function(){return c});var n=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(n){return i("div",{key:n,staticClass:"sticker",on:{click:function(i){i.stopPropagation(),i.preventDefault(),t.pick(e.path+n,e.meta.title)}}},[i("img",{attrs:{src:e.path+n}})])}),0)}),0)],1)},c=[]}}]); +//# sourceMappingURL=2.48f39bc510b7c0a7fca6.js.map \ No newline at end of file diff --git a/priv/static/static/js/2.48f39bc510b7c0a7fca6.js.map b/priv/static/static/js/2.48f39bc510b7c0a7fca6.js.map new file mode 100644 index 000000000..be87ffa46 --- /dev/null +++ b/priv/static/static/js/2.48f39bc510b7c0a7fca6.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack:///./src/components/sticker_picker/sticker_picker.vue","webpack:///./src/components/sticker_picker/sticker_picker.js","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.vue?8003"],"names":["__webpack_require__","r","__webpack_exports__","_babel_loader_sticker_picker_js__WEBPACK_IMPORTED_MODULE_0__","_babel_loader_sticker_picker_js__WEBPACK_IMPORTED_MODULE_0___default","n","__WEBPACK_IMPORT_KEY__","key","d","_node_modules_vue_loader_lib_template_compiler_index_id_data_v_70972e0c_hasScoped_false_optionsId_0_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_sticker_picker_vue__WEBPACK_IMPORTED_MODULE_1__","_node_modules_vue_loader_lib_runtime_component_normalizer__WEBPACK_IMPORTED_MODULE_2__","__vue_styles__","context","Component","Object","a","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","content","module","i","locals","exports","add","default","push","render","staticRenderFns","_vm","_h","$createElement","_c","_self","staticClass","attrs","render-only-focused","scrollable-tabs","_l","stickerpack","image-tooltip","title","image","tabIcon","on","click","$event","stopPropagation","preventDefault","src"],"mappings":"0FAAAA,EAAAC,EAAAC,GAAA,IAAAC,EAAAH,EAAA,KAAAI,EAAAJ,EAAAK,EAAAF,GAAA,QAAAG,KAAAH,EAAA,YAAAG,GAAA,SAAAC,GAAAP,EAAAQ,EAAAN,EAAAK,EAAA,kBAAAJ,EAAAI,KAAA,CAAAD,GAAA,IAAAG,EAAAT,EAAA,KAAAU,EAAAV,EAAA,GAQA,IAEAW,EAVA,SAAAC,GACEZ,EAAQ,MAeVa,EAAgBC,OAAAJ,EAAA,EAAAI,CACdV,EAAAW,EACAN,EAAA,EACAA,EAAA,GAXF,EAaAE,EATA,KAEA,MAYeT,EAAA,QAAAW,EAAiB,2FCzBhC,QAAAb,EAAA,yDAGA,IAAMgB,EAAgB,CACpBC,WAAY,CACVC,cAJJlB,EAAA,MAIIkB,SAEFC,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,UAAoBC,YAAY,CAAEZ,QAAOQ,aACtCN,KAAK,SAACW,GACLd,EAAKe,MAAM,WAAYD,GACvBd,EAAKJ,SACJ,SAACoB,GACFC,QAAQC,KAAK,wBACbD,QAAQC,KAAKF,GACbhB,EAAKe,MAAM,gBAAiB,8BAQ7BjC,uBChDf,IAAAqC,EAAcrD,EAAQ,KACtB,iBAAAqD,MAAA,EAA4CC,EAAAC,EAASF,EAAA,MACrDA,EAAAG,SAAAF,EAAAG,QAAAJ,EAAAG,SAGAE,EADU1D,EAAQ,GAAgE2D,SAClF,WAAAN,GAAA,4BCRAC,EAAAG,QAA2BzD,EAAQ,EAARA,EAA0D,IAKrF4D,KAAA,CAAcN,EAAAC,EAAS,oYAAoY,uCCL3ZvD,EAAAQ,EAAAN,EAAA,sBAAA2D,IAAA7D,EAAAQ,EAAAN,EAAA,sBAAA4D,IAAA,IAAAD,EAAA,WAA0B,IAAAE,EAAAtC,KAAauC,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,OAAiB3D,IAAAkE,EAAAnD,KAAA8C,YAAA,yBAAAC,MAAA,CAAiEK,gBAAAD,EAAArD,KAAAuD,MAAAC,MAAAH,EAAAnD,KAAAmD,EAAArD,KAAAyD,UAA4Fd,EAAAS,GAAAC,EAAArD,KAAA,kBAAAY,GAAsD,OAAAkC,EAAA,OAAiB3D,IAAAyB,EAAAoC,YAAA,UAAAU,GAAA,CAAsCC,MAAA,SAAAC,GAAyBA,EAAAC,kBAAyBD,EAAAE,iBAAwBnB,EAAAhC,KAAA0C,EAAAnD,KAAAU,EAAAyC,EAAArD,KAAAuD,UAA+D,CAAAT,EAAA,OAAYG,MAAA,CAAOc,IAAAV,EAAAnD,KAAAU,SAAsC,KAAK,QAC1vB8B,EAAA","file":"static/js/2.48f39bc510b7c0a7fca6.js","sourcesContent":["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!./sticker_picker.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./sticker_picker.js\"\nimport __vue_script__ from \"!!babel-loader!./sticker_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-70972e0c\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./sticker_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","/* eslint-env browser */\nimport statusPosterService from '../../services/status_poster/status_poster.service.js'\nimport TabSwitcher from '../tab_switcher/tab_switcher.js'\n\nconst StickerPicker = {\n components: {\n TabSwitcher\n },\n data () {\n return {\n meta: {\n stickers: []\n },\n path: ''\n }\n },\n computed: {\n pack () {\n return this.$store.state.instance.stickers || []\n }\n },\n methods: {\n clear () {\n this.meta = {\n stickers: []\n }\n },\n pick (sticker, name) {\n const store = this.$store\n // TODO remove this workaround by finding a way to bypass reuploads\n fetch(sticker)\n .then((res) => {\n res.blob().then((blob) => {\n var file = new File([blob], name, { mimetype: 'image/png' })\n var formData = new FormData()\n formData.append('file', file)\n statusPosterService.uploadMedia({ store, formData })\n .then((fileData) => {\n this.$emit('uploaded', fileData)\n this.clear()\n }, (error) => {\n console.warn(\"Can't attach sticker\")\n console.warn(error)\n this.$emit('upload-failed', 'default')\n })\n })\n })\n }\n }\n}\n\nexport default StickerPicker\n","// style-loader: Adds some css to the DOM by adding a \n","import FollowCard from '../follow_card/follow_card.vue'\nimport Conversation from '../conversation/conversation.vue'\nimport Status from '../status/status.vue'\nimport map from 'lodash/map'\n\nconst Search = {\n components: {\n FollowCard,\n Conversation,\n Status\n },\n props: [\n 'query'\n ],\n data () {\n return {\n loaded: false,\n loading: false,\n searchTerm: this.query || '',\n userIds: [],\n statuses: [],\n hashtags: [],\n currenResultTab: 'statuses'\n }\n },\n computed: {\n users () {\n return this.userIds.map(userId => this.$store.getters.findUser(userId))\n },\n visibleStatuses () {\n const allStatusesObject = this.$store.state.statuses.allStatusesObject\n\n return this.statuses.filter(status =>\n allStatusesObject[status.id] && !allStatusesObject[status.id].deleted\n )\n }\n },\n mounted () {\n this.search(this.query)\n },\n watch: {\n query (newValue) {\n this.searchTerm = newValue\n this.search(newValue)\n }\n },\n methods: {\n newQuery (query) {\n this.$router.push({ name: 'search', query: { query } })\n this.$refs.searchInput.focus()\n },\n search (query) {\n if (!query) {\n this.loading = false\n return\n }\n\n this.loading = true\n this.userIds = []\n this.statuses = []\n this.hashtags = []\n this.$refs.searchInput.blur()\n\n this.$store.dispatch('search', { q: query, resolve: true })\n .then(data => {\n this.loading = false\n this.userIds = map(data.accounts, 'id')\n this.statuses = data.statuses\n this.hashtags = data.hashtags\n this.currenResultTab = this.getActiveTab()\n this.loaded = true\n })\n },\n resultCount (tabName) {\n const length = this[tabName].length\n return length === 0 ? '' : ` (${length})`\n },\n onResultTabSwitch (key) {\n this.currenResultTab = key\n },\n getActiveTab () {\n if (this.visibleStatuses.length > 0) {\n return 'statuses'\n } else if (this.users.length > 0) {\n return 'people'\n } else if (this.hashtags.length > 0) {\n return 'hashtags'\n }\n\n return 'statuses'\n },\n lastHistoryRecord (hashtag) {\n return hashtag.history && hashtag.history[0]\n }\n }\n}\n\nexport default Search\n","/* eslint-env browser */\nimport { filter, trim } from 'lodash'\n\nimport TabSwitcher from '../tab_switcher/tab_switcher.js'\nimport StyleSwitcher from '../style_switcher/style_switcher.vue'\nimport InterfaceLanguageSwitcher from '../interface_language_switcher/interface_language_switcher.vue'\nimport { extractCommit } from '../../services/version/version.service'\n\nconst pleromaFeCommitUrl = 'https://git.pleroma.social/pleroma/pleroma-fe/commit/'\nconst pleromaBeCommitUrl = 'https://git.pleroma.social/pleroma/pleroma/commit/'\n\nconst settings = {\n data () {\n const user = this.$store.state.config\n const instance = this.$store.state.instance\n\n return {\n hideAttachmentsLocal: user.hideAttachments,\n padEmojiLocal: user.padEmoji,\n hideAttachmentsInConvLocal: user.hideAttachmentsInConv,\n maxThumbnails: user.maxThumbnails,\n hideNsfwLocal: user.hideNsfw,\n useOneClickNsfw: user.useOneClickNsfw,\n hideISPLocal: user.hideISP,\n preloadImage: user.preloadImage,\n\n hidePostStatsLocal: typeof user.hidePostStats === 'undefined'\n ? instance.hidePostStats\n : user.hidePostStats,\n hidePostStatsDefault: this.$t('settings.values.' + instance.hidePostStats),\n\n hideUserStatsLocal: typeof user.hideUserStats === 'undefined'\n ? instance.hideUserStats\n : user.hideUserStats,\n hideUserStatsDefault: this.$t('settings.values.' + instance.hideUserStats),\n\n hideFilteredStatusesLocal: typeof user.hideFilteredStatuses === 'undefined'\n ? instance.hideFilteredStatuses\n : user.hideFilteredStatuses,\n hideFilteredStatusesDefault: this.$t('settings.values.' + instance.hideFilteredStatuses),\n\n notificationVisibilityLocal: user.notificationVisibility,\n replyVisibilityLocal: user.replyVisibility,\n loopVideoLocal: user.loopVideo,\n muteWordsString: user.muteWords.join('\\n'),\n autoLoadLocal: user.autoLoad,\n streamingLocal: user.streaming,\n pauseOnUnfocusedLocal: user.pauseOnUnfocused,\n hoverPreviewLocal: user.hoverPreview,\n autohideFloatingPostButtonLocal: user.autohideFloatingPostButton,\n\n hideMutedPostsLocal: typeof user.hideMutedPosts === 'undefined'\n ? instance.hideMutedPosts\n : user.hideMutedPosts,\n hideMutedPostsDefault: this.$t('settings.values.' + instance.hideMutedPosts),\n\n collapseMessageWithSubjectLocal: typeof user.collapseMessageWithSubject === 'undefined'\n ? instance.collapseMessageWithSubject\n : user.collapseMessageWithSubject,\n collapseMessageWithSubjectDefault: this.$t('settings.values.' + instance.collapseMessageWithSubject),\n\n subjectLineBehaviorLocal: typeof user.subjectLineBehavior === 'undefined'\n ? instance.subjectLineBehavior\n : user.subjectLineBehavior,\n subjectLineBehaviorDefault: instance.subjectLineBehavior,\n\n postContentTypeLocal: typeof user.postContentType === 'undefined'\n ? instance.postContentType\n : user.postContentType,\n postContentTypeDefault: instance.postContentType,\n\n alwaysShowSubjectInputLocal: typeof user.alwaysShowSubjectInput === 'undefined'\n ? instance.alwaysShowSubjectInput\n : user.alwaysShowSubjectInput,\n alwaysShowSubjectInputDefault: this.$t('settings.values.' + instance.alwaysShowSubjectInput),\n\n scopeCopyLocal: typeof user.scopeCopy === 'undefined'\n ? instance.scopeCopy\n : user.scopeCopy,\n scopeCopyDefault: this.$t('settings.values.' + instance.scopeCopy),\n\n minimalScopesModeLocal: typeof user.minimalScopesMode === 'undefined'\n ? instance.minimalScopesMode\n : user.minimalScopesMode,\n minimalScopesModeDefault: this.$t('settings.values.' + instance.minimalScopesMode),\n\n stopGifs: user.stopGifs,\n webPushNotificationsLocal: user.webPushNotifications,\n loopVideoSilentOnlyLocal: user.loopVideosSilentOnly,\n loopSilentAvailable:\n // Firefox\n Object.getOwnPropertyDescriptor(HTMLVideoElement.prototype, 'mozHasAudio') ||\n // Chrome-likes\n Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'webkitAudioDecodedByteCount') ||\n // Future spec, still not supported in Nightly 63 as of 08/2018\n Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'audioTracks'),\n playVideosInModal: user.playVideosInModal,\n useContainFit: user.useContainFit,\n\n backendVersion: instance.backendVersion,\n frontendVersion: instance.frontendVersion\n }\n },\n components: {\n TabSwitcher,\n StyleSwitcher,\n InterfaceLanguageSwitcher\n },\n computed: {\n user () {\n return this.$store.state.users.currentUser\n },\n currentSaveStateNotice () {\n return this.$store.state.interface.settings.currentSaveStateNotice\n },\n postFormats () {\n return this.$store.state.instance.postFormats || []\n },\n instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel },\n frontendVersionLink () {\n return pleromaFeCommitUrl + this.frontendVersion\n },\n backendVersionLink () {\n return pleromaBeCommitUrl + extractCommit(this.backendVersion)\n }\n },\n watch: {\n hideAttachmentsLocal (value) {\n this.$store.dispatch('setOption', { name: 'hideAttachments', value })\n },\n padEmojiLocal (value) {\n this.$store.dispatch('setOption', { name: 'padEmoji', value })\n },\n hideAttachmentsInConvLocal (value) {\n this.$store.dispatch('setOption', { name: 'hideAttachmentsInConv', value })\n },\n hidePostStatsLocal (value) {\n this.$store.dispatch('setOption', { name: 'hidePostStats', value })\n },\n hideUserStatsLocal (value) {\n this.$store.dispatch('setOption', { name: 'hideUserStats', value })\n },\n hideFilteredStatusesLocal (value) {\n this.$store.dispatch('setOption', { name: 'hideFilteredStatuses', value })\n },\n hideNsfwLocal (value) {\n this.$store.dispatch('setOption', { name: 'hideNsfw', value })\n },\n useOneClickNsfw (value) {\n this.$store.dispatch('setOption', { name: 'useOneClickNsfw', value })\n },\n preloadImage (value) {\n this.$store.dispatch('setOption', { name: 'preloadImage', value })\n },\n hideISPLocal (value) {\n this.$store.dispatch('setOption', { name: 'hideISP', value })\n },\n 'notificationVisibilityLocal.likes' (value) {\n this.$store.dispatch('setOption', { name: 'notificationVisibility', value: this.$store.state.config.notificationVisibility })\n },\n 'notificationVisibilityLocal.follows' (value) {\n this.$store.dispatch('setOption', { name: 'notificationVisibility', value: this.$store.state.config.notificationVisibility })\n },\n 'notificationVisibilityLocal.repeats' (value) {\n this.$store.dispatch('setOption', { name: 'notificationVisibility', value: this.$store.state.config.notificationVisibility })\n },\n 'notificationVisibilityLocal.mentions' (value) {\n this.$store.dispatch('setOption', { name: 'notificationVisibility', value: this.$store.state.config.notificationVisibility })\n },\n replyVisibilityLocal (value) {\n this.$store.dispatch('setOption', { name: 'replyVisibility', value })\n },\n loopVideoLocal (value) {\n this.$store.dispatch('setOption', { name: 'loopVideo', value })\n },\n loopVideoSilentOnlyLocal (value) {\n this.$store.dispatch('setOption', { name: 'loopVideoSilentOnly', value })\n },\n autoLoadLocal (value) {\n this.$store.dispatch('setOption', { name: 'autoLoad', value })\n },\n streamingLocal (value) {\n this.$store.dispatch('setOption', { name: 'streaming', value })\n },\n pauseOnUnfocusedLocal (value) {\n this.$store.dispatch('setOption', { name: 'pauseOnUnfocused', value })\n },\n hoverPreviewLocal (value) {\n this.$store.dispatch('setOption', { name: 'hoverPreview', value })\n },\n autohideFloatingPostButtonLocal (value) {\n this.$store.dispatch('setOption', { name: 'autohideFloatingPostButton', value })\n },\n muteWordsString (value) {\n value = filter(value.split('\\n'), (word) => trim(word).length > 0)\n this.$store.dispatch('setOption', { name: 'muteWords', value })\n },\n hideMutedPostsLocal (value) {\n this.$store.dispatch('setOption', { name: 'hideMutedPosts', value })\n },\n collapseMessageWithSubjectLocal (value) {\n this.$store.dispatch('setOption', { name: 'collapseMessageWithSubject', value })\n },\n scopeCopyLocal (value) {\n this.$store.dispatch('setOption', { name: 'scopeCopy', value })\n },\n alwaysShowSubjectInputLocal (value) {\n this.$store.dispatch('setOption', { name: 'alwaysShowSubjectInput', value })\n },\n subjectLineBehaviorLocal (value) {\n this.$store.dispatch('setOption', { name: 'subjectLineBehavior', value })\n },\n postContentTypeLocal (value) {\n this.$store.dispatch('setOption', { name: 'postContentType', value })\n },\n minimalScopesModeLocal (value) {\n this.$store.dispatch('setOption', { name: 'minimalScopesMode', value })\n },\n stopGifs (value) {\n this.$store.dispatch('setOption', { name: 'stopGifs', value })\n },\n webPushNotificationsLocal (value) {\n this.$store.dispatch('setOption', { name: 'webPushNotifications', value })\n if (value) this.$store.dispatch('registerPushNotifications')\n },\n playVideosInModal (value) {\n this.$store.dispatch('setOption', { name: 'playVideosInModal', value })\n },\n useContainFit (value) {\n this.$store.dispatch('setOption', { name: 'useContainFit', value })\n },\n maxThumbnails (value) {\n value = this.maxThumbnails = Math.floor(Math.max(value, 0))\n this.$store.dispatch('setOption', { name: 'maxThumbnails', value })\n }\n }\n}\n\nexport default settings\n","import { rgb2hex, hex2rgb, getContrastRatio, alphaBlend } from '../../services/color_convert/color_convert.js'\nimport { set, delete as del } from 'vue'\nimport { generateColors, generateShadows, generateRadii, generateFonts, composePreset, getThemes } from '../../services/style_setter/style_setter.js'\nimport ColorInput from '../color_input/color_input.vue'\nimport RangeInput from '../range_input/range_input.vue'\nimport OpacityInput from '../opacity_input/opacity_input.vue'\nimport ShadowControl from '../shadow_control/shadow_control.vue'\nimport FontControl from '../font_control/font_control.vue'\nimport ContrastRatio from '../contrast_ratio/contrast_ratio.vue'\nimport TabSwitcher from '../tab_switcher/tab_switcher.js'\nimport Preview from './preview.vue'\nimport ExportImport from '../export_import/export_import.vue'\n\n// List of color values used in v1\nconst v1OnlyNames = [\n 'bg',\n 'fg',\n 'text',\n 'link',\n 'cRed',\n 'cGreen',\n 'cBlue',\n 'cOrange'\n].map(_ => _ + 'ColorLocal')\n\nexport default {\n data () {\n return {\n availableStyles: [],\n selected: this.$store.state.config.theme,\n\n previewShadows: {},\n previewColors: {},\n previewRadii: {},\n previewFonts: {},\n\n shadowsInvalid: true,\n colorsInvalid: true,\n radiiInvalid: true,\n\n keepColor: false,\n keepShadows: false,\n keepOpacity: false,\n keepRoundness: false,\n keepFonts: false,\n\n textColorLocal: '',\n linkColorLocal: '',\n\n bgColorLocal: '',\n bgOpacityLocal: undefined,\n\n fgColorLocal: '',\n fgTextColorLocal: undefined,\n fgLinkColorLocal: undefined,\n\n btnColorLocal: undefined,\n btnTextColorLocal: undefined,\n btnOpacityLocal: undefined,\n\n inputColorLocal: undefined,\n inputTextColorLocal: undefined,\n inputOpacityLocal: undefined,\n\n panelColorLocal: undefined,\n panelTextColorLocal: undefined,\n panelLinkColorLocal: undefined,\n panelFaintColorLocal: undefined,\n panelOpacityLocal: undefined,\n\n topBarColorLocal: undefined,\n topBarTextColorLocal: undefined,\n topBarLinkColorLocal: undefined,\n\n alertErrorColorLocal: undefined,\n\n badgeOpacityLocal: undefined,\n badgeNotificationColorLocal: undefined,\n\n borderColorLocal: undefined,\n borderOpacityLocal: undefined,\n\n faintColorLocal: undefined,\n faintOpacityLocal: undefined,\n faintLinkColorLocal: undefined,\n\n cRedColorLocal: '',\n cBlueColorLocal: '',\n cGreenColorLocal: '',\n cOrangeColorLocal: '',\n\n shadowSelected: undefined,\n shadowsLocal: {},\n fontsLocal: {},\n\n btnRadiusLocal: '',\n inputRadiusLocal: '',\n checkboxRadiusLocal: '',\n panelRadiusLocal: '',\n avatarRadiusLocal: '',\n avatarAltRadiusLocal: '',\n attachmentRadiusLocal: '',\n tooltipRadiusLocal: ''\n }\n },\n created () {\n const self = this\n\n getThemes().then((themesComplete) => {\n self.availableStyles = themesComplete\n })\n },\n mounted () {\n this.normalizeLocalState(this.$store.state.config.customTheme)\n if (typeof this.shadowSelected === 'undefined') {\n this.shadowSelected = this.shadowsAvailable[0]\n }\n },\n computed: {\n selectedVersion () {\n return Array.isArray(this.selected) ? 1 : 2\n },\n currentColors () {\n return {\n bg: this.bgColorLocal,\n text: this.textColorLocal,\n link: this.linkColorLocal,\n\n fg: this.fgColorLocal,\n fgText: this.fgTextColorLocal,\n fgLink: this.fgLinkColorLocal,\n\n panel: this.panelColorLocal,\n panelText: this.panelTextColorLocal,\n panelLink: this.panelLinkColorLocal,\n panelFaint: this.panelFaintColorLocal,\n\n input: this.inputColorLocal,\n inputText: this.inputTextColorLocal,\n\n topBar: this.topBarColorLocal,\n topBarText: this.topBarTextColorLocal,\n topBarLink: this.topBarLinkColorLocal,\n\n btn: this.btnColorLocal,\n btnText: this.btnTextColorLocal,\n\n alertError: this.alertErrorColorLocal,\n badgeNotification: this.badgeNotificationColorLocal,\n\n faint: this.faintColorLocal,\n faintLink: this.faintLinkColorLocal,\n border: this.borderColorLocal,\n\n cRed: this.cRedColorLocal,\n cBlue: this.cBlueColorLocal,\n cGreen: this.cGreenColorLocal,\n cOrange: this.cOrangeColorLocal\n }\n },\n currentOpacity () {\n return {\n bg: this.bgOpacityLocal,\n btn: this.btnOpacityLocal,\n input: this.inputOpacityLocal,\n panel: this.panelOpacityLocal,\n topBar: this.topBarOpacityLocal,\n border: this.borderOpacityLocal,\n faint: this.faintOpacityLocal\n }\n },\n currentRadii () {\n return {\n btn: this.btnRadiusLocal,\n input: this.inputRadiusLocal,\n checkbox: this.checkboxRadiusLocal,\n panel: this.panelRadiusLocal,\n avatar: this.avatarRadiusLocal,\n avatarAlt: this.avatarAltRadiusLocal,\n tooltip: this.tooltipRadiusLocal,\n attachment: this.attachmentRadiusLocal\n }\n },\n preview () {\n return composePreset(this.previewColors, this.previewRadii, this.previewShadows, this.previewFonts)\n },\n previewTheme () {\n if (!this.preview.theme.colors) return { colors: {}, opacity: {}, radii: {}, shadows: {}, fonts: {} }\n return this.preview.theme\n },\n // This needs optimization maybe\n previewContrast () {\n if (!this.previewTheme.colors.bg) return {}\n const colors = this.previewTheme.colors\n const opacity = this.previewTheme.opacity\n if (!colors.bg) return {}\n const hints = (ratio) => ({\n text: ratio.toPrecision(3) + ':1',\n // AA level, AAA level\n aa: ratio >= 4.5,\n aaa: ratio >= 7,\n // same but for 18pt+ texts\n laa: ratio >= 3,\n laaa: ratio >= 4.5\n })\n\n // fgsfds :DDDD\n const fgs = {\n text: hex2rgb(colors.text),\n panelText: hex2rgb(colors.panelText),\n panelLink: hex2rgb(colors.panelLink),\n btnText: hex2rgb(colors.btnText),\n topBarText: hex2rgb(colors.topBarText),\n inputText: hex2rgb(colors.inputText),\n\n link: hex2rgb(colors.link),\n topBarLink: hex2rgb(colors.topBarLink),\n\n red: hex2rgb(colors.cRed),\n green: hex2rgb(colors.cGreen),\n blue: hex2rgb(colors.cBlue),\n orange: hex2rgb(colors.cOrange)\n }\n\n const bgs = {\n bg: hex2rgb(colors.bg),\n btn: hex2rgb(colors.btn),\n panel: hex2rgb(colors.panel),\n topBar: hex2rgb(colors.topBar),\n input: hex2rgb(colors.input),\n alertError: hex2rgb(colors.alertError),\n badgeNotification: hex2rgb(colors.badgeNotification)\n }\n\n /* This is a bit confusing because \"bottom layer\" used is text color\n * This is done to get worst case scenario when background below transparent\n * layer matches text color, making it harder to read the lower alpha is.\n */\n const ratios = {\n bgText: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.text), fgs.text),\n bgLink: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.link), fgs.link),\n bgRed: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.red), fgs.red),\n bgGreen: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.green), fgs.green),\n bgBlue: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.blue), fgs.blue),\n bgOrange: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.orange), fgs.orange),\n\n tintText: getContrastRatio(alphaBlend(bgs.bg, 0.5, fgs.panelText), fgs.text),\n\n panelText: getContrastRatio(alphaBlend(bgs.panel, opacity.panel, fgs.panelText), fgs.panelText),\n panelLink: getContrastRatio(alphaBlend(bgs.panel, opacity.panel, fgs.panelLink), fgs.panelLink),\n\n btnText: getContrastRatio(alphaBlend(bgs.btn, opacity.btn, fgs.btnText), fgs.btnText),\n\n inputText: getContrastRatio(alphaBlend(bgs.input, opacity.input, fgs.inputText), fgs.inputText),\n\n topBarText: getContrastRatio(alphaBlend(bgs.topBar, opacity.topBar, fgs.topBarText), fgs.topBarText),\n topBarLink: getContrastRatio(alphaBlend(bgs.topBar, opacity.topBar, fgs.topBarLink), fgs.topBarLink)\n }\n\n return Object.entries(ratios).reduce((acc, [k, v]) => { acc[k] = hints(v); return acc }, {})\n },\n previewRules () {\n if (!this.preview.rules) return ''\n return [\n ...Object.values(this.preview.rules),\n 'color: var(--text)',\n 'font-family: var(--interfaceFont, sans-serif)'\n ].join(';')\n },\n shadowsAvailable () {\n return Object.keys(this.previewTheme.shadows).sort()\n },\n currentShadowOverriden: {\n get () {\n return !!this.currentShadow\n },\n set (val) {\n if (val) {\n set(this.shadowsLocal, this.shadowSelected, this.currentShadowFallback.map(_ => Object.assign({}, _)))\n } else {\n del(this.shadowsLocal, this.shadowSelected)\n }\n }\n },\n currentShadowFallback () {\n return this.previewTheme.shadows[this.shadowSelected]\n },\n currentShadow: {\n get () {\n return this.shadowsLocal[this.shadowSelected]\n },\n set (v) {\n set(this.shadowsLocal, this.shadowSelected, v)\n }\n },\n themeValid () {\n return !this.shadowsInvalid && !this.colorsInvalid && !this.radiiInvalid\n },\n exportedTheme () {\n const saveEverything = (\n !this.keepFonts &&\n !this.keepShadows &&\n !this.keepOpacity &&\n !this.keepRoundness &&\n !this.keepColor\n )\n\n const theme = {}\n\n if (this.keepFonts || saveEverything) {\n theme.fonts = this.fontsLocal\n }\n if (this.keepShadows || saveEverything) {\n theme.shadows = this.shadowsLocal\n }\n if (this.keepOpacity || saveEverything) {\n theme.opacity = this.currentOpacity\n }\n if (this.keepColor || saveEverything) {\n theme.colors = this.currentColors\n }\n if (this.keepRoundness || saveEverything) {\n theme.radii = this.currentRadii\n }\n\n return {\n // To separate from other random JSON files and possible future theme formats\n _pleroma_theme_version: 2, theme\n }\n }\n },\n components: {\n ColorInput,\n OpacityInput,\n RangeInput,\n ContrastRatio,\n ShadowControl,\n FontControl,\n TabSwitcher,\n Preview,\n ExportImport\n },\n methods: {\n setCustomTheme () {\n this.$store.dispatch('setOption', {\n name: 'customTheme',\n value: {\n shadows: this.shadowsLocal,\n fonts: this.fontsLocal,\n opacity: this.currentOpacity,\n colors: this.currentColors,\n radii: this.currentRadii\n }\n })\n },\n onImport (parsed) {\n if (parsed._pleroma_theme_version === 1) {\n this.normalizeLocalState(parsed, 1)\n } else if (parsed._pleroma_theme_version === 2) {\n this.normalizeLocalState(parsed.theme, 2)\n }\n },\n importValidator (parsed) {\n const version = parsed._pleroma_theme_version\n return version >= 1 || version <= 2\n },\n clearAll () {\n const state = this.$store.state.config.customTheme\n const version = state.colors ? 2 : 'l1'\n this.normalizeLocalState(this.$store.state.config.customTheme, version)\n },\n\n // Clears all the extra stuff when loading V1 theme\n clearV1 () {\n Object.keys(this.$data)\n .filter(_ => _.endsWith('ColorLocal') || _.endsWith('OpacityLocal'))\n .filter(_ => !v1OnlyNames.includes(_))\n .forEach(key => {\n set(this.$data, key, undefined)\n })\n },\n\n clearRoundness () {\n Object.keys(this.$data)\n .filter(_ => _.endsWith('RadiusLocal'))\n .forEach(key => {\n set(this.$data, key, undefined)\n })\n },\n\n clearOpacity () {\n Object.keys(this.$data)\n .filter(_ => _.endsWith('OpacityLocal'))\n .forEach(key => {\n set(this.$data, key, undefined)\n })\n },\n\n clearShadows () {\n this.shadowsLocal = {}\n },\n\n clearFonts () {\n this.fontsLocal = {}\n },\n\n /**\n * This applies stored theme data onto form. Supports three versions of data:\n * v2 (version = 2) - newer version of themes.\n * v1 (version = 1) - older version of themes (import from file)\n * v1l (version = l1) - older version of theme (load from local storage)\n * v1 and v1l differ because of way themes were stored/exported.\n * @param {Object} input - input data\n * @param {Number} version - version of data. 0 means try to guess based on data. \"l1\" means v1, locastorage type\n */\n normalizeLocalState (input, version = 0) {\n const colors = input.colors || input\n const radii = input.radii || input\n const opacity = input.opacity\n const shadows = input.shadows || {}\n const fonts = input.fonts || {}\n\n if (version === 0) {\n if (input.version) version = input.version\n // Old v1 naming: fg is text, btn is foreground\n if (typeof colors.text === 'undefined' && typeof colors.fg !== 'undefined') {\n version = 1\n }\n // New v2 naming: text is text, fg is foreground\n if (typeof colors.text !== 'undefined' && typeof colors.fg !== 'undefined') {\n version = 2\n }\n }\n\n // Stuff that differs between V1 and V2\n if (version === 1) {\n this.fgColorLocal = rgb2hex(colors.btn)\n this.textColorLocal = rgb2hex(colors.fg)\n }\n\n if (!this.keepColor) {\n this.clearV1()\n const keys = new Set(version !== 1 ? Object.keys(colors) : [])\n if (version === 1 || version === 'l1') {\n keys\n .add('bg')\n .add('link')\n .add('cRed')\n .add('cBlue')\n .add('cGreen')\n .add('cOrange')\n }\n\n keys.forEach(key => {\n this[key + 'ColorLocal'] = rgb2hex(colors[key])\n })\n }\n\n if (!this.keepRoundness) {\n this.clearRoundness()\n Object.entries(radii).forEach(([k, v]) => {\n // 'Radius' is kept mostly for v1->v2 localstorage transition\n const key = k.endsWith('Radius') ? k.split('Radius')[0] : k\n this[key + 'RadiusLocal'] = v\n })\n }\n\n if (!this.keepShadows) {\n this.clearShadows()\n this.shadowsLocal = shadows\n this.shadowSelected = this.shadowsAvailable[0]\n }\n\n if (!this.keepFonts) {\n this.clearFonts()\n this.fontsLocal = fonts\n }\n\n if (opacity && !this.keepOpacity) {\n this.clearOpacity()\n Object.entries(opacity).forEach(([k, v]) => {\n if (typeof v === 'undefined' || v === null || Number.isNaN(v)) return\n this[k + 'OpacityLocal'] = v\n })\n }\n }\n },\n watch: {\n currentRadii () {\n try {\n this.previewRadii = generateRadii({ radii: this.currentRadii })\n this.radiiInvalid = false\n } catch (e) {\n this.radiiInvalid = true\n console.warn(e)\n }\n },\n shadowsLocal: {\n handler () {\n try {\n this.previewShadows = generateShadows({ shadows: this.shadowsLocal })\n this.shadowsInvalid = false\n } catch (e) {\n this.shadowsInvalid = true\n console.warn(e)\n }\n },\n deep: true\n },\n fontsLocal: {\n handler () {\n try {\n this.previewFonts = generateFonts({ fonts: this.fontsLocal })\n this.fontsInvalid = false\n } catch (e) {\n this.fontsInvalid = true\n console.warn(e)\n }\n },\n deep: true\n },\n currentColors () {\n try {\n this.previewColors = generateColors({\n opacity: this.currentOpacity,\n colors: this.currentColors\n })\n this.colorsInvalid = false\n } catch (e) {\n this.colorsInvalid = true\n console.warn(e)\n }\n },\n currentOpacity () {\n try {\n this.previewColors = generateColors({\n opacity: this.currentOpacity,\n colors: this.currentColors\n })\n } catch (e) {\n console.warn(e)\n }\n },\n selected () {\n if (this.selectedVersion === 1) {\n if (!this.keepRoundness) {\n this.clearRoundness()\n }\n\n if (!this.keepShadows) {\n this.clearShadows()\n }\n\n if (!this.keepOpacity) {\n this.clearOpacity()\n }\n\n if (!this.keepColor) {\n this.clearV1()\n\n this.bgColorLocal = this.selected[1]\n this.fgColorLocal = this.selected[2]\n this.textColorLocal = this.selected[3]\n this.linkColorLocal = this.selected[4]\n this.cRedColorLocal = this.selected[5]\n this.cGreenColorLocal = this.selected[6]\n this.cBlueColorLocal = this.selected[7]\n this.cOrangeColorLocal = this.selected[8]\n }\n } else if (this.selectedVersion >= 2) {\n this.normalizeLocalState(this.selected.theme, 2)\n }\n }\n }\n}\n","\n\n\n\n\n","\n\n\n","\n\n\n","import ColorInput from '../color_input/color_input.vue'\nimport OpacityInput from '../opacity_input/opacity_input.vue'\nimport { getCssShadow } from '../../services/style_setter/style_setter.js'\nimport { hex2rgb } from '../../services/color_convert/color_convert.js'\n\nexport default {\n // 'Value' and 'Fallback' can be undefined, but if they are\n // initially vue won't detect it when they become something else\n // therefore i'm using \"ready\" which should be passed as true when\n // data becomes available\n props: [\n 'value', 'fallback', 'ready'\n ],\n data () {\n return {\n selectedId: 0,\n // TODO there are some bugs regarding display of array (it's not getting updated when deleting for some reason)\n cValue: this.value || this.fallback || []\n }\n },\n components: {\n ColorInput,\n OpacityInput\n },\n methods: {\n add () {\n this.cValue.push(Object.assign({}, this.selected))\n this.selectedId = this.cValue.length - 1\n },\n del () {\n this.cValue.splice(this.selectedId, 1)\n this.selectedId = this.cValue.length === 0 ? undefined : this.selectedId - 1\n },\n moveUp () {\n const movable = this.cValue.splice(this.selectedId, 1)[0]\n this.cValue.splice(this.selectedId - 1, 0, movable)\n this.selectedId -= 1\n },\n moveDn () {\n const movable = this.cValue.splice(this.selectedId, 1)[0]\n this.cValue.splice(this.selectedId + 1, 0, movable)\n this.selectedId += 1\n }\n },\n beforeUpdate () {\n this.cValue = this.value || this.fallback\n },\n computed: {\n selected () {\n if (this.ready && this.cValue.length > 0) {\n return this.cValue[this.selectedId]\n } else {\n return {\n x: 0,\n y: 0,\n blur: 0,\n spread: 0,\n inset: false,\n color: '#000000',\n alpha: 1\n }\n }\n },\n moveUpValid () {\n return this.ready && this.selectedId > 0\n },\n moveDnValid () {\n return this.ready && this.selectedId < this.cValue.length - 1\n },\n present () {\n return this.ready &&\n typeof this.cValue[this.selectedId] !== 'undefined' &&\n !this.usingFallback\n },\n usingFallback () {\n return typeof this.value === 'undefined'\n },\n rgb () {\n return hex2rgb(this.selected.color)\n },\n style () {\n return this.ready ? {\n boxShadow: getCssShadow(this.cValue)\n } : {}\n }\n }\n}\n","import { set } from 'vue'\n\nexport default {\n props: [\n 'name', 'label', 'value', 'fallback', 'options', 'no-inherit'\n ],\n data () {\n return {\n lValue: this.value,\n availableOptions: [\n this.noInherit ? '' : 'inherit',\n 'custom',\n ...(this.options || []),\n 'serif',\n 'monospace',\n 'sans-serif'\n ].filter(_ => _)\n }\n },\n beforeUpdate () {\n this.lValue = this.value\n },\n computed: {\n present () {\n return typeof this.lValue !== 'undefined'\n },\n dValue () {\n return this.lValue || this.fallback || {}\n },\n family: {\n get () {\n return this.dValue.family\n },\n set (v) {\n set(this.lValue, 'family', v)\n this.$emit('input', this.lValue)\n }\n },\n isCustom () {\n return this.preset === 'custom'\n },\n preset: {\n get () {\n if (this.family === 'serif' ||\n this.family === 'sans-serif' ||\n this.family === 'monospace' ||\n this.family === 'inherit') {\n return this.family\n } else {\n return 'custom'\n }\n },\n set (v) {\n this.family = v === 'custom' ? '' : v\n }\n }\n }\n}\n","\n\n\n\n\n","\n\n\n\n\n","\n\n\n","import { validationMixin } from 'vuelidate'\nimport { required, sameAs } from 'vuelidate/lib/validators'\nimport { mapActions, mapState } from 'vuex'\n\nconst registration = {\n mixins: [validationMixin],\n data: () => ({\n user: {\n email: '',\n fullname: '',\n username: '',\n password: '',\n confirm: ''\n },\n captcha: {}\n }),\n validations: {\n user: {\n email: { required },\n username: { required },\n fullname: { required },\n password: { required },\n confirm: {\n required,\n sameAsPassword: sameAs('password')\n }\n }\n },\n created () {\n if ((!this.registrationOpen && !this.token) || this.signedIn) {\n this.$router.push({ name: 'root' })\n }\n\n this.setCaptcha()\n },\n computed: {\n token () { return this.$route.params.token },\n bioPlaceholder () {\n return this.$t('registration.bio_placeholder').replace(/\\s*\\n\\s*/g, ' \\n')\n },\n ...mapState({\n registrationOpen: (state) => state.instance.registrationOpen,\n signedIn: (state) => !!state.users.currentUser,\n isPending: (state) => state.users.signUpPending,\n serverValidationErrors: (state) => state.users.signUpErrors,\n termsOfService: (state) => state.instance.tos\n })\n },\n methods: {\n ...mapActions(['signUp', 'getCaptcha']),\n async submit () {\n this.user.nickname = this.user.username\n this.user.token = this.token\n\n this.user.captcha_solution = this.captcha.solution\n this.user.captcha_token = this.captcha.token\n this.user.captcha_answer_data = this.captcha.answer_data\n\n this.$v.$touch()\n\n if (!this.$v.$invalid) {\n try {\n await this.signUp(this.user)\n this.$router.push({ name: 'friends' })\n } catch (error) {\n console.warn('Registration failed: ' + error)\n }\n }\n },\n setCaptcha () {\n this.getCaptcha().then(cpt => { this.captcha = cpt })\n }\n }\n}\n\nexport default registration\n","import { mapState } from 'vuex'\nimport passwordResetApi from '../../services/new_api/password_reset.js'\n\nconst passwordReset = {\n data: () => ({\n user: {\n email: ''\n },\n isPending: false,\n success: false,\n throttled: false,\n error: null\n }),\n computed: {\n ...mapState({\n signedIn: (state) => !!state.users.currentUser,\n instance: state => state.instance\n }),\n mailerEnabled () {\n return this.instance.mailerEnabled\n }\n },\n created () {\n if (this.signedIn) {\n this.$router.push({ name: 'root' })\n }\n },\n methods: {\n dismissError () {\n this.error = null\n },\n submit () {\n this.isPending = true\n const email = this.user.email\n const instance = this.instance.server\n\n passwordResetApi({ instance, email }).then(({ status }) => {\n this.isPending = false\n this.user.email = ''\n\n if (status === 204) {\n this.success = true\n this.error = null\n } else if (status === 404 || status === 400) {\n this.error = this.$t('password_reset.not_found')\n this.$nextTick(() => {\n this.$refs.email.focus()\n })\n } else if (status === 429) {\n this.throttled = true\n this.error = this.$t('password_reset.too_many_requests')\n }\n }).catch(() => {\n this.isPending = false\n this.user.email = ''\n this.error = this.$t('general.generic_error')\n })\n }\n }\n}\n\nexport default passwordReset\n","import unescape from 'lodash/unescape'\nimport get from 'lodash/get'\nimport map from 'lodash/map'\nimport reject from 'lodash/reject'\nimport TabSwitcher from '../tab_switcher/tab_switcher.js'\nimport ImageCropper from '../image_cropper/image_cropper.vue'\nimport StyleSwitcher from '../style_switcher/style_switcher.vue'\nimport ScopeSelector from '../scope_selector/scope_selector.vue'\nimport fileSizeFormatService from '../../services/file_size_format/file_size_format.js'\nimport BlockCard from '../block_card/block_card.vue'\nimport MuteCard from '../mute_card/mute_card.vue'\nimport SelectableList from '../selectable_list/selectable_list.vue'\nimport ProgressButton from '../progress_button/progress_button.vue'\nimport EmojiInput from '../emoji_input/emoji_input.vue'\nimport suggestor from '../emoji_input/suggestor.js'\nimport Autosuggest from '../autosuggest/autosuggest.vue'\nimport Importer from '../importer/importer.vue'\nimport Exporter from '../exporter/exporter.vue'\nimport withSubscription from '../../hocs/with_subscription/with_subscription'\nimport Mfa from './mfa.vue'\n\nconst BlockList = withSubscription({\n fetch: (props, $store) => $store.dispatch('fetchBlocks'),\n select: (props, $store) => get($store.state.users.currentUser, 'blockIds', []),\n childPropName: 'items'\n})(SelectableList)\n\nconst MuteList = withSubscription({\n fetch: (props, $store) => $store.dispatch('fetchMutes'),\n select: (props, $store) => get($store.state.users.currentUser, 'muteIds', []),\n childPropName: 'items'\n})(SelectableList)\n\nconst UserSettings = {\n data () {\n return {\n newName: this.$store.state.users.currentUser.name,\n newBio: unescape(this.$store.state.users.currentUser.description),\n newLocked: this.$store.state.users.currentUser.locked,\n newNoRichText: this.$store.state.users.currentUser.no_rich_text,\n newDefaultScope: this.$store.state.users.currentUser.default_scope,\n hideFollows: this.$store.state.users.currentUser.hide_follows,\n hideFollowers: this.$store.state.users.currentUser.hide_followers,\n hideFollowsCount: this.$store.state.users.currentUser.hide_follows_count,\n hideFollowersCount: this.$store.state.users.currentUser.hide_followers_count,\n showRole: this.$store.state.users.currentUser.show_role,\n role: this.$store.state.users.currentUser.role,\n discoverable: this.$store.state.users.currentUser.discoverable,\n pickAvatarBtnVisible: true,\n bannerUploading: false,\n backgroundUploading: false,\n banner: null,\n bannerPreview: null,\n background: null,\n backgroundPreview: null,\n bannerUploadError: null,\n backgroundUploadError: null,\n deletingAccount: false,\n deleteAccountConfirmPasswordInput: '',\n deleteAccountError: false,\n changePasswordInputs: [ '', '', '' ],\n changedPassword: false,\n changePasswordError: false,\n activeTab: 'profile',\n notificationSettings: this.$store.state.users.currentUser.notification_settings\n }\n },\n created () {\n this.$store.dispatch('fetchTokens')\n },\n components: {\n StyleSwitcher,\n ScopeSelector,\n TabSwitcher,\n ImageCropper,\n BlockList,\n MuteList,\n EmojiInput,\n Autosuggest,\n BlockCard,\n MuteCard,\n ProgressButton,\n Importer,\n Exporter,\n Mfa\n },\n computed: {\n user () {\n return this.$store.state.users.currentUser\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({ emoji: [\n ...this.$store.state.instance.emoji,\n ...this.$store.state.instance.customEmoji\n ] })\n },\n pleromaBackend () {\n return this.$store.state.instance.pleromaBackend\n },\n minimalScopesMode () {\n return this.$store.state.instance.minimalScopesMode\n },\n vis () {\n return {\n public: { selected: this.newDefaultScope === 'public' },\n unlisted: { selected: this.newDefaultScope === 'unlisted' },\n private: { selected: this.newDefaultScope === 'private' },\n direct: { selected: this.newDefaultScope === 'direct' }\n }\n },\n currentSaveStateNotice () {\n return this.$store.state.interface.settings.currentSaveStateNotice\n },\n oauthTokens () {\n return this.$store.state.oauthTokens.tokens.map(oauthToken => {\n return {\n id: oauthToken.id,\n appName: oauthToken.app_name,\n validUntil: new Date(oauthToken.valid_until).toLocaleDateString()\n }\n })\n }\n },\n methods: {\n updateProfile () {\n this.$store.state.api.backendInteractor\n .updateProfile({\n params: {\n note: this.newBio,\n locked: this.newLocked,\n // Backend notation.\n /* eslint-disable camelcase */\n display_name: this.newName,\n default_scope: this.newDefaultScope,\n no_rich_text: this.newNoRichText,\n hide_follows: this.hideFollows,\n hide_followers: this.hideFollowers,\n discoverable: this.discoverable,\n hide_follows_count: this.hideFollowsCount,\n hide_followers_count: this.hideFollowersCount,\n show_role: this.showRole\n /* eslint-enable camelcase */\n } }).then((user) => {\n this.$store.commit('addNewUsers', [user])\n this.$store.commit('setCurrentUser', user)\n })\n },\n updateNotificationSettings () {\n this.$store.state.api.backendInteractor\n .updateNotificationSettings({ settings: this.notificationSettings })\n },\n changeVis (visibility) {\n this.newDefaultScope = visibility\n },\n uploadFile (slot, e) {\n const file = e.target.files[0]\n if (!file) { return }\n if (file.size > this.$store.state.instance[slot + 'limit']) {\n const filesize = fileSizeFormatService.fileSizeFormat(file.size)\n const allowedsize = fileSizeFormatService.fileSizeFormat(this.$store.state.instance[slot + 'limit'])\n this[slot + 'UploadError'] = this.$t('upload.error.base') + ' ' + this.$t('upload.error.file_too_big', { filesize: filesize.num, filesizeunit: filesize.unit, allowedsize: allowedsize.num, allowedsizeunit: allowedsize.unit })\n return\n }\n // eslint-disable-next-line no-undef\n const reader = new FileReader()\n reader.onload = ({ target }) => {\n const img = target.result\n this[slot + 'Preview'] = img\n this[slot] = file\n }\n reader.readAsDataURL(file)\n },\n submitAvatar (cropper, file) {\n const that = this\n return new Promise((resolve, reject) => {\n function updateAvatar (avatar) {\n that.$store.state.api.backendInteractor.updateAvatar({ avatar })\n .then((user) => {\n that.$store.commit('addNewUsers', [user])\n that.$store.commit('setCurrentUser', user)\n resolve()\n })\n .catch((err) => {\n reject(new Error(that.$t('upload.error.base') + ' ' + err.message))\n })\n }\n\n if (cropper) {\n cropper.getCroppedCanvas().toBlob(updateAvatar, file.type)\n } else {\n updateAvatar(file)\n }\n })\n },\n clearUploadError (slot) {\n this[slot + 'UploadError'] = null\n },\n submitBanner () {\n if (!this.bannerPreview) { return }\n\n this.bannerUploading = true\n this.$store.state.api.backendInteractor.updateBanner({ banner: this.banner })\n .then((user) => {\n this.$store.commit('addNewUsers', [user])\n this.$store.commit('setCurrentUser', user)\n this.bannerPreview = null\n })\n .catch((err) => {\n this.bannerUploadError = this.$t('upload.error.base') + ' ' + err.message\n })\n .then(() => { this.bannerUploading = false })\n },\n submitBg () {\n if (!this.backgroundPreview) { return }\n let background = this.background\n this.backgroundUploading = true\n this.$store.state.api.backendInteractor.updateBg({ background }).then((data) => {\n if (!data.error) {\n this.$store.commit('addNewUsers', [data])\n this.$store.commit('setCurrentUser', data)\n this.backgroundPreview = null\n } else {\n this.backgroundUploadError = this.$t('upload.error.base') + data.error\n }\n this.backgroundUploading = false\n })\n },\n importFollows (file) {\n return this.$store.state.api.backendInteractor.importFollows(file)\n .then((status) => {\n if (!status) {\n throw new Error('failed')\n }\n })\n },\n importBlocks (file) {\n return this.$store.state.api.backendInteractor.importBlocks(file)\n .then((status) => {\n if (!status) {\n throw new Error('failed')\n }\n })\n },\n generateExportableUsersContent (users) {\n // Get addresses\n return users.map((user) => {\n // check is it's a local user\n if (user && user.is_local) {\n // append the instance address\n // eslint-disable-next-line no-undef\n return user.screen_name + '@' + location.hostname\n }\n return user.screen_name\n }).join('\\n')\n },\n getFollowsContent () {\n return this.$store.state.api.backendInteractor.exportFriends({ id: this.$store.state.users.currentUser.id })\n .then(this.generateExportableUsersContent)\n },\n getBlocksContent () {\n return this.$store.state.api.backendInteractor.fetchBlocks()\n .then(this.generateExportableUsersContent)\n },\n confirmDelete () {\n this.deletingAccount = true\n },\n deleteAccount () {\n this.$store.state.api.backendInteractor.deleteAccount({ password: this.deleteAccountConfirmPasswordInput })\n .then((res) => {\n if (res.status === 'success') {\n this.$store.dispatch('logout')\n this.$router.push({ name: 'root' })\n } else {\n this.deleteAccountError = res.error\n }\n })\n },\n changePassword () {\n const params = {\n password: this.changePasswordInputs[0],\n newPassword: this.changePasswordInputs[1],\n newPasswordConfirmation: this.changePasswordInputs[2]\n }\n this.$store.state.api.backendInteractor.changePassword(params)\n .then((res) => {\n if (res.status === 'success') {\n this.changedPassword = true\n this.changePasswordError = false\n this.logout()\n } else {\n this.changedPassword = false\n this.changePasswordError = res.error\n }\n })\n },\n activateTab (tabName) {\n this.activeTab = tabName\n },\n logout () {\n this.$store.dispatch('logout')\n this.$router.replace('/')\n },\n revokeToken (id) {\n if (window.confirm(`${this.$i18n.t('settings.revoke_token')}?`)) {\n this.$store.dispatch('revokeToken', id)\n }\n },\n filterUnblockedUsers (userIds) {\n return reject(userIds, (userId) => {\n const user = this.$store.getters.findUser(userId)\n return !user || user.statusnet_blocking || user.id === this.$store.state.users.currentUser.id\n })\n },\n filterUnMutedUsers (userIds) {\n return reject(userIds, (userId) => {\n const user = this.$store.getters.findUser(userId)\n return !user || user.muted || user.id === this.$store.state.users.currentUser.id\n })\n },\n queryUserIds (query) {\n return this.$store.dispatch('searchUsers', query)\n .then((users) => map(users, 'id'))\n },\n blockUsers (ids) {\n return this.$store.dispatch('blockUsers', ids)\n },\n unblockUsers (ids) {\n return this.$store.dispatch('unblockUsers', ids)\n },\n muteUsers (ids) {\n return this.$store.dispatch('muteUsers', ids)\n },\n unmuteUsers (ids) {\n return this.$store.dispatch('unmuteUsers', ids)\n },\n identity (value) {\n return value\n }\n }\n}\n\nexport default UserSettings\n","import Cropper from 'cropperjs'\nimport 'cropperjs/dist/cropper.css'\n\nconst ImageCropper = {\n props: {\n trigger: {\n type: [String, window.Element],\n required: true\n },\n submitHandler: {\n type: Function,\n required: true\n },\n cropperOptions: {\n type: Object,\n default () {\n return {\n aspectRatio: 1,\n autoCropArea: 1,\n viewMode: 1,\n movable: false,\n zoomable: false,\n guides: false\n }\n }\n },\n mimes: {\n type: String,\n default: 'image/png, image/gif, image/jpeg, image/bmp, image/x-icon'\n },\n saveButtonLabel: {\n type: String\n },\n saveWithoutCroppingButtonlabel: {\n type: String\n },\n cancelButtonLabel: {\n type: String\n }\n },\n data () {\n return {\n cropper: undefined,\n dataUrl: undefined,\n filename: undefined,\n submitting: false,\n submitError: null\n }\n },\n computed: {\n saveText () {\n return this.saveButtonLabel || this.$t('image_cropper.save')\n },\n saveWithoutCroppingText () {\n return this.saveWithoutCroppingButtonlabel || this.$t('image_cropper.save_without_cropping')\n },\n cancelText () {\n return this.cancelButtonLabel || this.$t('image_cropper.cancel')\n },\n submitErrorMsg () {\n return this.submitError && this.submitError instanceof Error ? this.submitError.toString() : this.submitError\n }\n },\n methods: {\n destroy () {\n if (this.cropper) {\n this.cropper.destroy()\n }\n this.$refs.input.value = ''\n this.dataUrl = undefined\n this.$emit('close')\n },\n submit (cropping = true) {\n this.submitting = true\n this.avatarUploadError = null\n this.submitHandler(cropping && this.cropper, this.file)\n .then(() => this.destroy())\n .catch((err) => {\n this.submitError = err\n })\n .finally(() => {\n this.submitting = false\n })\n },\n pickImage () {\n this.$refs.input.click()\n },\n createCropper () {\n this.cropper = new Cropper(this.$refs.img, this.cropperOptions)\n },\n getTriggerDOM () {\n return typeof this.trigger === 'object' ? this.trigger : document.querySelector(this.trigger)\n },\n readFile () {\n const fileInput = this.$refs.input\n if (fileInput.files != null && fileInput.files[0] != null) {\n this.file = fileInput.files[0]\n let reader = new window.FileReader()\n reader.onload = (e) => {\n this.dataUrl = e.target.result\n this.$emit('open')\n }\n reader.readAsDataURL(this.file)\n this.$emit('changed', this.file, reader)\n }\n },\n clearError () {\n this.submitError = null\n }\n },\n mounted () {\n // listen for click event on trigger\n const trigger = this.getTriggerDOM()\n if (!trigger) {\n this.$emit('error', 'No image make trigger found.', 'user')\n } else {\n trigger.addEventListener('click', this.pickImage)\n }\n // listen for input file changes\n const fileInput = this.$refs.input\n fileInput.addEventListener('change', this.readFile)\n },\n beforeDestroy: function () {\n // remove the event listeners\n const trigger = this.getTriggerDOM()\n if (trigger) {\n trigger.removeEventListener('click', this.pickImage)\n }\n const fileInput = this.$refs.input\n fileInput.removeEventListener('change', this.readFile)\n }\n}\n\nexport default ImageCropper\n","import BasicUserCard from '../basic_user_card/basic_user_card.vue'\n\nconst BlockCard = {\n props: ['userId'],\n data () {\n return {\n progress: false\n }\n },\n computed: {\n user () {\n return this.$store.getters.findUser(this.userId)\n },\n blocked () {\n return this.user.statusnet_blocking\n }\n },\n components: {\n BasicUserCard\n },\n methods: {\n unblockUser () {\n this.progress = true\n this.$store.dispatch('unblockUser', this.user.id).then(() => {\n this.progress = false\n })\n },\n blockUser () {\n this.progress = true\n this.$store.dispatch('blockUser', this.user.id).then(() => {\n this.progress = false\n })\n }\n }\n}\n\nexport default BlockCard\n","import BasicUserCard from '../basic_user_card/basic_user_card.vue'\n\nconst MuteCard = {\n props: ['userId'],\n data () {\n return {\n progress: false\n }\n },\n computed: {\n user () {\n return this.$store.getters.findUser(this.userId)\n },\n muted () {\n return this.user.muted\n }\n },\n components: {\n BasicUserCard\n },\n methods: {\n unmuteUser () {\n this.progress = true\n this.$store.dispatch('unmuteUser', this.user.id).then(() => {\n this.progress = false\n })\n },\n muteUser () {\n this.progress = true\n this.$store.dispatch('muteUser', this.user.id).then(() => {\n this.progress = false\n })\n }\n }\n}\n\nexport default MuteCard\n","import List from '../list/list.vue'\nimport Checkbox from '../checkbox/checkbox.vue'\n\nconst SelectableList = {\n components: {\n List,\n Checkbox\n },\n props: {\n items: {\n type: Array,\n default: () => []\n },\n getKey: {\n type: Function,\n default: item => item.id\n }\n },\n data () {\n return {\n selected: []\n }\n },\n computed: {\n allKeys () {\n return this.items.map(this.getKey)\n },\n filteredSelected () {\n return this.allKeys.filter(key => this.selected.indexOf(key) !== -1)\n },\n allSelected () {\n return this.filteredSelected.length === this.items.length\n },\n noneSelected () {\n return this.filteredSelected.length === 0\n },\n someSelected () {\n return !this.allSelected && !this.noneSelected\n }\n },\n methods: {\n isSelected (item) {\n return this.filteredSelected.indexOf(this.getKey(item)) !== -1\n },\n toggle (checked, item) {\n const key = this.getKey(item)\n const oldChecked = this.isSelected(key)\n if (checked !== oldChecked) {\n if (checked) {\n this.selected.push(key)\n } else {\n this.selected.splice(this.selected.indexOf(key), 1)\n }\n }\n },\n toggleAll (value) {\n if (value) {\n this.selected = this.allKeys.slice(0)\n } else {\n this.selected = []\n }\n }\n }\n}\n\nexport default SelectableList\n","\n\n\n\n\n","const debounceMilliseconds = 500\n\nexport default {\n props: {\n query: { // function to query results and return a promise\n type: Function,\n required: true\n },\n filter: { // function to filter results in real time\n type: Function\n },\n placeholder: {\n type: String,\n default: 'Search...'\n }\n },\n data () {\n return {\n term: '',\n timeout: null,\n results: [],\n resultsVisible: false\n }\n },\n computed: {\n filtered () {\n return this.filter ? this.filter(this.results) : this.results\n }\n },\n watch: {\n term (val) {\n this.fetchResults(val)\n }\n },\n methods: {\n fetchResults (term) {\n clearTimeout(this.timeout)\n this.timeout = setTimeout(() => {\n this.results = []\n if (term) {\n this.query(term).then((results) => { this.results = results })\n }\n }, debounceMilliseconds)\n },\n onInputClick () {\n this.resultsVisible = true\n },\n onClickOutside () {\n this.resultsVisible = false\n }\n }\n}\n","const Importer = {\n props: {\n submitHandler: {\n type: Function,\n required: true\n },\n submitButtonLabel: {\n type: String,\n default () {\n return this.$t('importer.submit')\n }\n },\n successMessage: {\n type: String,\n default () {\n return this.$t('importer.success')\n }\n },\n errorMessage: {\n type: String,\n default () {\n return this.$t('importer.error')\n }\n }\n },\n data () {\n return {\n file: null,\n error: false,\n success: false,\n submitting: false\n }\n },\n methods: {\n change () {\n this.file = this.$refs.input.files[0]\n },\n submit () {\n this.dismiss()\n this.submitting = true\n this.submitHandler(this.file)\n .then(() => { this.success = true })\n .catch(() => { this.error = true })\n .finally(() => { this.submitting = false })\n },\n dismiss () {\n this.success = false\n this.error = false\n }\n }\n}\n\nexport default Importer\n","const Exporter = {\n props: {\n getContent: {\n type: Function,\n required: true\n },\n filename: {\n type: String,\n default: 'export.csv'\n },\n exportButtonLabel: {\n type: String,\n default () {\n return this.$t('exporter.export')\n }\n },\n processingMessage: {\n type: String,\n default () {\n return this.$t('exporter.processing')\n }\n }\n },\n data () {\n return {\n processing: false\n }\n },\n methods: {\n process () {\n this.processing = true\n this.getContent()\n .then((content) => {\n const fileToDownload = document.createElement('a')\n fileToDownload.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(content))\n fileToDownload.setAttribute('download', this.filename)\n fileToDownload.style.display = 'none'\n document.body.appendChild(fileToDownload)\n fileToDownload.click()\n document.body.removeChild(fileToDownload)\n // Add delay before hiding processing state since browser takes some time to handle file download\n setTimeout(() => { this.processing = false }, 2000)\n })\n }\n }\n}\n\nexport default Exporter\n","import RecoveryCodes from './mfa_backup_codes.vue'\nimport TOTP from './mfa_totp.vue'\nimport Confirm from './confirm.vue'\nimport VueQrcode from '@chenfengyuan/vue-qrcode'\nimport { mapState } from 'vuex'\n\nconst Mfa = {\n data: () => ({\n settings: { // current settings of MFA\n available: false,\n enabled: false,\n totp: false\n },\n setupState: { // setup mfa\n state: '', // state of setup. '' -> 'getBackupCodes' -> 'setupOTP' -> 'complete'\n setupOTPState: '' // state of setup otp. '' -> 'prepare' -> 'confirm' -> 'complete'\n },\n backupCodes: {\n getNewCodes: false,\n inProgress: false, // progress of fetch codes\n codes: []\n },\n otpSettings: { // pre-setup setting of OTP. secret key, qrcode url.\n provisioning_uri: '',\n key: ''\n },\n currentPassword: null,\n otpConfirmToken: null,\n error: null,\n readyInit: false\n }),\n components: {\n 'recovery-codes': RecoveryCodes,\n 'totp-item': TOTP,\n 'qrcode': VueQrcode,\n 'confirm': Confirm\n },\n computed: {\n canSetupOTP () {\n return (\n (this.setupInProgress && this.backupCodesPrepared) ||\n this.settings.enabled\n ) && !this.settings.totp && !this.setupOTPInProgress\n },\n setupInProgress () {\n return this.setupState.state !== '' && this.setupState.state !== 'complete'\n },\n setupOTPInProgress () {\n return this.setupState.state === 'setupOTP' && !this.completedOTP\n },\n prepareOTP () {\n return this.setupState.setupOTPState === 'prepare'\n },\n confirmOTP () {\n return this.setupState.setupOTPState === 'confirm'\n },\n completedOTP () {\n return this.setupState.setupOTPState === 'completed'\n },\n backupCodesPrepared () {\n return !this.backupCodes.inProgress && this.backupCodes.codes.length > 0\n },\n confirmNewBackupCodes () {\n return this.backupCodes.getNewCodes\n },\n ...mapState({\n backendInteractor: (state) => state.api.backendInteractor\n })\n },\n\n methods: {\n activateOTP () {\n if (!this.settings.enabled) {\n this.setupState.state = 'getBackupcodes'\n this.fetchBackupCodes()\n }\n },\n fetchBackupCodes () {\n this.backupCodes.inProgress = true\n this.backupCodes.codes = []\n\n return this.backendInteractor.generateMfaBackupCodes()\n .then((res) => {\n this.backupCodes.codes = res.codes\n this.backupCodes.inProgress = false\n })\n },\n getBackupCodes () { // get a new backup codes\n this.backupCodes.getNewCodes = true\n },\n confirmBackupCodes () { // confirm getting new backup codes\n this.fetchBackupCodes().then((res) => {\n this.backupCodes.getNewCodes = false\n })\n },\n cancelBackupCodes () { // cancel confirm form of new backup codes\n this.backupCodes.getNewCodes = false\n },\n\n // Setup OTP\n setupOTP () { // prepare setup OTP\n this.setupState.state = 'setupOTP'\n this.setupState.setupOTPState = 'prepare'\n this.backendInteractor.mfaSetupOTP()\n .then((res) => {\n this.otpSettings = res\n this.setupState.setupOTPState = 'confirm'\n })\n },\n doConfirmOTP () { // handler confirm enable OTP\n this.error = null\n this.backendInteractor.mfaConfirmOTP({\n token: this.otpConfirmToken,\n password: this.currentPassword\n })\n .then((res) => {\n if (res.error) {\n this.error = res.error\n return\n }\n this.completeSetup()\n })\n },\n\n completeSetup () {\n this.setupState.setupOTPState = 'complete'\n this.setupState.state = 'complete'\n this.currentPassword = null\n this.error = null\n this.fetchSettings()\n },\n cancelSetup () { // cancel setup\n this.setupState.setupOTPState = ''\n this.setupState.state = ''\n this.currentPassword = null\n this.error = null\n },\n // end Setup OTP\n\n // fetch settings from server\n async fetchSettings () {\n let result = await this.backendInteractor.fetchSettingsMFA()\n if (result.error) return\n this.settings = result.settings\n this.settings.available = true\n return result\n }\n },\n mounted () {\n this.fetchSettings().then(() => {\n this.readyInit = true\n })\n }\n}\nexport default Mfa\n","export default {\n props: {\n backupCodes: {\n type: Object,\n default: () => ({\n inProgress: false,\n codes: []\n })\n }\n },\n data: () => ({}),\n computed: {\n inProgress () { return this.backupCodes.inProgress },\n ready () { return this.backupCodes.codes.length > 0 },\n displayTitle () { return this.inProgress || this.ready }\n }\n}\n","import Confirm from './confirm.vue'\nimport { mapState } from 'vuex'\n\nexport default {\n props: ['settings'],\n data: () => ({\n error: false,\n currentPassword: '',\n deactivate: false,\n inProgress: false // progress peform request to disable otp method\n }),\n components: {\n 'confirm': Confirm\n },\n computed: {\n isActivated () {\n return this.settings.totp\n },\n ...mapState({\n backendInteractor: (state) => state.api.backendInteractor\n })\n },\n methods: {\n doActivate () {\n this.$emit('activate')\n },\n cancelDeactivate () { this.deactivate = false },\n doDeactivate () {\n this.error = null\n this.deactivate = true\n },\n confirmDeactivate () { // confirm deactivate TOTP method\n this.error = null\n this.inProgress = true\n this.backendInteractor.mfaDisableOTP({\n password: this.currentPassword\n })\n .then((res) => {\n this.inProgress = false\n if (res.error) {\n this.error = res.error\n return\n }\n this.deactivate = false\n this.$emit('deactivate')\n })\n }\n }\n}\n","const Confirm = {\n props: ['disabled'],\n data: () => ({}),\n methods: {\n confirm () { this.$emit('confirm') },\n cancel () { this.$emit('cancel') }\n }\n}\nexport default Confirm\n","import FollowRequestCard from '../follow_request_card/follow_request_card.vue'\n\nconst FollowRequests = {\n components: {\n FollowRequestCard\n },\n computed: {\n requests () {\n return this.$store.state.api.followRequests\n }\n }\n}\n\nexport default FollowRequests\n","import BasicUserCard from '../basic_user_card/basic_user_card.vue'\n\nconst FollowRequestCard = {\n props: ['user'],\n components: {\n BasicUserCard\n },\n methods: {\n approveUser () {\n this.$store.state.api.backendInteractor.approveUser(this.user.id)\n this.$store.dispatch('removeFollowRequest', this.user)\n },\n denyUser () {\n this.$store.state.api.backendInteractor.denyUser(this.user.id)\n this.$store.dispatch('removeFollowRequest', this.user)\n }\n }\n}\n\nexport default FollowRequestCard\n","import oauth from '../../services/new_api/oauth.js'\n\nconst oac = {\n props: ['code'],\n mounted () {\n if (this.code) {\n const { clientId, clientSecret } = this.$store.state.oauth\n\n oauth.getToken({\n clientId,\n clientSecret,\n instance: this.$store.state.instance.server,\n code: this.code\n }).then((result) => {\n this.$store.commit('setToken', result.access_token)\n this.$store.dispatch('loginUser', result.access_token)\n this.$router.push({ name: 'friends' })\n })\n }\n }\n}\n\nexport default oac\n","import { mapState, mapGetters, mapActions, mapMutations } from 'vuex'\nimport oauthApi from '../../services/new_api/oauth.js'\n\nconst LoginForm = {\n data: () => ({\n user: {},\n error: false\n }),\n computed: {\n isPasswordAuth () { return this.requiredPassword },\n isTokenAuth () { return this.requiredToken },\n ...mapState({\n registrationOpen: state => state.instance.registrationOpen,\n instance: state => state.instance,\n loggingIn: state => state.users.loggingIn,\n oauth: state => state.oauth\n }),\n ...mapGetters(\n 'authFlow', ['requiredPassword', 'requiredToken', 'requiredMFA']\n )\n },\n methods: {\n ...mapMutations('authFlow', ['requireMFA']),\n ...mapActions({ login: 'authFlow/login' }),\n submit () {\n this.isTokenAuth ? this.submitToken() : this.submitPassword()\n },\n submitToken () {\n const { clientId, clientSecret } = this.oauth\n const data = {\n clientId,\n clientSecret,\n instance: this.instance.server,\n commit: this.$store.commit\n }\n\n oauthApi.getOrCreateApp(data)\n .then((app) => { oauthApi.login({ ...app, ...data }) })\n },\n submitPassword () {\n const { clientId } = this.oauth\n const data = {\n clientId,\n oauth: this.oauth,\n instance: this.instance.server,\n commit: this.$store.commit\n }\n this.error = false\n\n oauthApi.getOrCreateApp(data).then((app) => {\n oauthApi.getTokenWithCredentials(\n {\n ...app,\n instance: data.instance,\n username: this.user.username,\n password: this.user.password\n }\n ).then((result) => {\n if (result.error) {\n if (result.error === 'mfa_required') {\n this.requireMFA({ app: app, settings: result })\n } else {\n this.error = result.error\n this.focusOnPasswordInput()\n }\n return\n }\n this.login(result).then(() => {\n this.$router.push({ name: 'friends' })\n })\n })\n })\n },\n clearError () { this.error = false },\n focusOnPasswordInput () {\n let passwordInput = this.$refs.passwordInput\n passwordInput.focus()\n passwordInput.setSelectionRange(0, passwordInput.value.length)\n }\n }\n}\n\nexport default LoginForm\n","import mfaApi from '../../services/new_api/mfa.js'\nimport { mapState, mapGetters, mapActions, mapMutations } from 'vuex'\n\nexport default {\n data: () => ({\n code: null,\n error: false\n }),\n computed: {\n ...mapGetters({\n authApp: 'authFlow/app',\n authSettings: 'authFlow/settings'\n }),\n ...mapState({ instance: 'instance' })\n },\n methods: {\n ...mapMutations('authFlow', ['requireTOTP', 'abortMFA']),\n ...mapActions({ login: 'authFlow/login' }),\n clearError () { this.error = false },\n submit () {\n const data = {\n app: this.authApp,\n instance: this.instance.server,\n mfaToken: this.authSettings.mfa_token,\n code: this.code\n }\n\n mfaApi.verifyRecoveryCode(data).then((result) => {\n if (result.error) {\n this.error = result.error\n this.code = null\n return\n }\n\n this.login(result).then(() => {\n this.$router.push({ name: 'friends' })\n })\n })\n }\n }\n}\n","import mfaApi from '../../services/new_api/mfa.js'\nimport { mapState, mapGetters, mapActions, mapMutations } from 'vuex'\nexport default {\n data: () => ({\n code: null,\n error: false\n }),\n computed: {\n ...mapGetters({\n authApp: 'authFlow/app',\n authSettings: 'authFlow/settings'\n }),\n ...mapState({ instance: 'instance' })\n },\n methods: {\n ...mapMutations('authFlow', ['requireRecovery', 'abortMFA']),\n ...mapActions({ login: 'authFlow/login' }),\n clearError () { this.error = false },\n submit () {\n const data = {\n app: this.authApp,\n instance: this.instance.server,\n mfaToken: this.authSettings.mfa_token,\n code: this.code\n }\n\n mfaApi.verifyOTPCode(data).then((result) => {\n if (result.error) {\n this.error = result.error\n this.code = null\n return\n }\n\n this.login(result).then(() => {\n this.$router.push({ name: 'friends' })\n })\n })\n }\n }\n}\n","import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'\n\nconst chatPanel = {\n props: [ 'floating' ],\n data () {\n return {\n currentMessage: '',\n channel: null,\n collapsed: true\n }\n },\n computed: {\n messages () {\n return this.$store.state.chat.messages\n }\n },\n methods: {\n submit (message) {\n this.$store.state.chat.channel.push('new_msg', { text: message }, 10000)\n this.currentMessage = ''\n },\n togglePanel () {\n this.collapsed = !this.collapsed\n },\n userProfileLink (user) {\n return generateProfileLink(user.id, user.username, this.$store.state.instance.restrictedNicknames)\n }\n }\n}\n\nexport default chatPanel\n","import apiService from '../../services/api/api.service.js'\nimport FollowCard from '../follow_card/follow_card.vue'\n\nconst WhoToFollow = {\n components: {\n FollowCard\n },\n data () {\n return {\n users: []\n }\n },\n mounted () {\n this.getWhoToFollow()\n },\n methods: {\n showWhoToFollow (reply) {\n reply.forEach((i, index) => {\n this.$store.state.api.backendInteractor.fetchUser({ id: i.acct })\n .then((externalUser) => {\n if (!externalUser.error) {\n this.$store.commit('addNewUsers', [externalUser])\n this.users.push(externalUser)\n }\n })\n })\n },\n getWhoToFollow () {\n const credentials = this.$store.state.users.currentUser.credentials\n if (credentials) {\n apiService.suggestions({ credentials: credentials })\n .then((reply) => {\n this.showWhoToFollow(reply)\n })\n }\n }\n }\n}\n\nexport default WhoToFollow\n","import InstanceSpecificPanel from '../instance_specific_panel/instance_specific_panel.vue'\nimport FeaturesPanel from '../features_panel/features_panel.vue'\nimport TermsOfServicePanel from '../terms_of_service_panel/terms_of_service_panel.vue'\n\nconst About = {\n components: {\n InstanceSpecificPanel,\n FeaturesPanel,\n TermsOfServicePanel\n },\n computed: {\n showFeaturesPanel () { return this.$store.state.instance.showFeaturesPanel }\n }\n}\n\nexport default About\n","const InstanceSpecificPanel = {\n computed: {\n instanceSpecificPanelContent () {\n return this.$store.state.instance.instanceSpecificPanelContent\n }\n }\n}\n\nexport default InstanceSpecificPanel\n","const FeaturesPanel = {\n computed: {\n chat: function () { return this.$store.state.instance.chatAvailable },\n gopher: function () { return this.$store.state.instance.gopherAvailable },\n whoToFollow: function () { return this.$store.state.instance.suggestionsEnabled },\n mediaProxy: function () { return this.$store.state.instance.mediaProxyAvailable },\n minimalScopesMode: function () { return this.$store.state.instance.minimalScopesMode },\n textlimit: function () { return this.$store.state.instance.textlimit }\n }\n}\n\nexport default FeaturesPanel\n","const TermsOfServicePanel = {\n computed: {\n content () {\n return this.$store.state.instance.tos\n }\n }\n}\n\nexport default TermsOfServicePanel\n","import UserPanel from './components/user_panel/user_panel.vue'\nimport NavPanel from './components/nav_panel/nav_panel.vue'\nimport Notifications from './components/notifications/notifications.vue'\nimport SearchBar from './components/search_bar/search_bar.vue'\nimport InstanceSpecificPanel from './components/instance_specific_panel/instance_specific_panel.vue'\nimport FeaturesPanel from './components/features_panel/features_panel.vue'\nimport WhoToFollowPanel from './components/who_to_follow_panel/who_to_follow_panel.vue'\nimport ChatPanel from './components/chat_panel/chat_panel.vue'\nimport MediaModal from './components/media_modal/media_modal.vue'\nimport SideDrawer from './components/side_drawer/side_drawer.vue'\nimport MobilePostStatusButton from './components/mobile_post_status_button/mobile_post_status_button.vue'\nimport MobileNav from './components/mobile_nav/mobile_nav.vue'\nimport UserReportingModal from './components/user_reporting_modal/user_reporting_modal.vue'\nimport PostStatusModal from './components/post_status_modal/post_status_modal.vue'\nimport { windowWidth } from './services/window_utils/window_utils'\n\nexport default {\n name: 'app',\n components: {\n UserPanel,\n NavPanel,\n Notifications,\n SearchBar,\n InstanceSpecificPanel,\n FeaturesPanel,\n WhoToFollowPanel,\n ChatPanel,\n MediaModal,\n SideDrawer,\n MobilePostStatusButton,\n MobileNav,\n UserReportingModal,\n PostStatusModal\n },\n data: () => ({\n mobileActivePanel: 'timeline',\n searchBarHidden: true,\n supportsMask: window.CSS && window.CSS.supports && (\n window.CSS.supports('mask-size', 'contain') ||\n window.CSS.supports('-webkit-mask-size', 'contain') ||\n window.CSS.supports('-moz-mask-size', 'contain') ||\n window.CSS.supports('-ms-mask-size', 'contain') ||\n window.CSS.supports('-o-mask-size', 'contain')\n )\n }),\n created () {\n // Load the locale from the storage\n this.$i18n.locale = this.$store.state.config.interfaceLanguage\n window.addEventListener('resize', this.updateMobileState)\n },\n destroyed () {\n window.removeEventListener('resize', this.updateMobileState)\n },\n computed: {\n currentUser () { return this.$store.state.users.currentUser },\n background () {\n return this.currentUser.background_image || this.$store.state.instance.background\n },\n enableMask () { return this.supportsMask && this.$store.state.instance.logoMask },\n logoStyle () {\n return {\n 'visibility': this.enableMask ? 'hidden' : 'visible'\n }\n },\n logoMaskStyle () {\n return this.enableMask ? {\n 'mask-image': `url(${this.$store.state.instance.logo})`\n } : {\n 'background-color': this.enableMask ? '' : 'transparent'\n }\n },\n logoBgStyle () {\n return Object.assign({\n 'margin': `${this.$store.state.instance.logoMargin} 0`,\n opacity: this.searchBarHidden ? 1 : 0\n }, this.enableMask ? {} : {\n 'background-color': this.enableMask ? '' : 'transparent'\n })\n },\n logo () { return this.$store.state.instance.logo },\n bgStyle () {\n return {\n 'background-image': `url(${this.background})`\n }\n },\n bgAppStyle () {\n return {\n '--body-background-image': `url(${this.background})`\n }\n },\n sitename () { return this.$store.state.instance.name },\n chat () { return this.$store.state.chat.channel.state === 'joined' },\n suggestionsEnabled () { return this.$store.state.instance.suggestionsEnabled },\n showInstanceSpecificPanel () {\n return this.$store.state.instance.showInstanceSpecificPanel &&\n !this.$store.state.config.hideISP &&\n this.$store.state.instance.instanceSpecificPanelContent\n },\n showFeaturesPanel () { return this.$store.state.instance.showFeaturesPanel },\n isMobileLayout () { return this.$store.state.interface.mobileLayout }\n },\n methods: {\n scrollToTop () {\n window.scrollTo(0, 0)\n },\n logout () {\n this.$router.replace('/main/public')\n this.$store.dispatch('logout')\n },\n onSearchBarToggled (hidden) {\n this.searchBarHidden = hidden\n },\n updateMobileState () {\n const mobileLayout = windowWidth() <= 800\n const changed = mobileLayout !== this.isMobileLayout\n if (changed) {\n this.$store.dispatch('setMobileLayout', mobileLayout)\n }\n }\n }\n}\n","import AuthForm from '../auth_form/auth_form.js'\nimport PostStatusForm from '../post_status_form/post_status_form.vue'\nimport UserCard from '../user_card/user_card.vue'\nimport { mapState } from 'vuex'\n\nconst UserPanel = {\n computed: {\n signedIn () { return this.user },\n ...mapState({ user: state => state.users.currentUser })\n },\n components: {\n AuthForm,\n PostStatusForm,\n UserCard\n }\n}\n\nexport default UserPanel\n","import followRequestFetcher from '../../services/follow_request_fetcher/follow_request_fetcher.service'\n\nconst NavPanel = {\n created () {\n if (this.currentUser && this.currentUser.locked) {\n const store = this.$store\n const credentials = store.state.users.currentUser.credentials\n\n followRequestFetcher.startFetching({ store, credentials })\n }\n },\n computed: {\n currentUser () {\n return this.$store.state.users.currentUser\n },\n chat () {\n return this.$store.state.chat.channel\n },\n followRequestCount () {\n return this.$store.state.api.followRequests.length\n }\n }\n}\n\nexport default NavPanel\n","const SearchBar = {\n data: () => ({\n searchTerm: undefined,\n hidden: true,\n error: false,\n loading: false\n }),\n watch: {\n '$route': function (route) {\n if (route.name === 'search') {\n this.searchTerm = route.query.query\n }\n }\n },\n methods: {\n find (searchTerm) {\n this.$router.push({ name: 'search', query: { query: searchTerm } })\n this.$refs.searchInput.focus()\n },\n toggleHidden () {\n this.hidden = !this.hidden\n this.$emit('toggled', this.hidden)\n this.$nextTick(() => {\n if (!this.hidden) {\n this.$refs.searchInput.focus()\n }\n })\n }\n }\n}\n\nexport default SearchBar\n","import apiService from '../../services/api/api.service.js'\nimport generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'\nimport { shuffle } from 'lodash'\n\nfunction showWhoToFollow (panel, reply) {\n const shuffled = shuffle(reply)\n\n panel.usersToFollow.forEach((toFollow, index) => {\n let user = shuffled[index]\n let img = user.avatar || '/images/avi.png'\n let name = user.acct\n\n toFollow.img = img\n toFollow.name = name\n\n panel.$store.state.api.backendInteractor.fetchUser({ id: name })\n .then((externalUser) => {\n if (!externalUser.error) {\n panel.$store.commit('addNewUsers', [externalUser])\n toFollow.id = externalUser.id\n }\n })\n })\n}\n\nfunction getWhoToFollow (panel) {\n var credentials = panel.$store.state.users.currentUser.credentials\n if (credentials) {\n panel.usersToFollow.forEach(toFollow => {\n toFollow.name = 'Loading...'\n })\n apiService.suggestions({ credentials: credentials })\n .then((reply) => {\n showWhoToFollow(panel, reply)\n })\n }\n}\n\nconst WhoToFollowPanel = {\n data: () => ({\n usersToFollow: new Array(3).fill().map(x => (\n {\n img: '/images/avi.png',\n name: '',\n id: 0\n }\n ))\n }),\n computed: {\n user: function () {\n return this.$store.state.users.currentUser.screen_name\n },\n suggestionsEnabled () {\n return this.$store.state.instance.suggestionsEnabled\n }\n },\n methods: {\n userProfileLink (id, name) {\n return generateProfileLink(id, name, this.$store.state.instance.restrictedNicknames)\n }\n },\n watch: {\n user: function (user, oldUser) {\n if (this.suggestionsEnabled) {\n getWhoToFollow(this)\n }\n }\n },\n mounted:\n function () {\n if (this.suggestionsEnabled) {\n getWhoToFollow(this)\n }\n }\n}\n\nexport default WhoToFollowPanel\n","import StillImage from '../still-image/still-image.vue'\nimport VideoAttachment from '../video_attachment/video_attachment.vue'\nimport fileTypeService from '../../services/file_type/file_type.service.js'\n\nconst MediaModal = {\n components: {\n StillImage,\n VideoAttachment\n },\n computed: {\n showing () {\n return this.$store.state.mediaViewer.activated\n },\n media () {\n return this.$store.state.mediaViewer.media\n },\n currentIndex () {\n return this.$store.state.mediaViewer.currentIndex\n },\n currentMedia () {\n return this.media[this.currentIndex]\n },\n canNavigate () {\n return this.media.length > 1\n },\n type () {\n return this.currentMedia ? fileTypeService.fileType(this.currentMedia.mimetype) : null\n }\n },\n methods: {\n hide () {\n this.$store.dispatch('closeMediaViewer')\n },\n goPrev () {\n if (this.canNavigate) {\n const prevIndex = this.currentIndex === 0 ? this.media.length - 1 : (this.currentIndex - 1)\n this.$store.dispatch('setCurrent', this.media[prevIndex])\n }\n },\n goNext () {\n if (this.canNavigate) {\n const nextIndex = this.currentIndex === this.media.length - 1 ? 0 : (this.currentIndex + 1)\n this.$store.dispatch('setCurrent', this.media[nextIndex])\n }\n },\n handleKeyupEvent (e) {\n if (this.showing && e.keyCode === 27) { // escape\n this.hide()\n }\n },\n handleKeydownEvent (e) {\n if (!this.showing) {\n return\n }\n\n if (e.keyCode === 39) { // arrow right\n this.goNext()\n } else if (e.keyCode === 37) { // arrow left\n this.goPrev()\n }\n }\n },\n mounted () {\n document.addEventListener('keyup', this.handleKeyupEvent)\n document.addEventListener('keydown', this.handleKeydownEvent)\n },\n destroyed () {\n document.removeEventListener('keyup', this.handleKeyupEvent)\n document.removeEventListener('keydown', this.handleKeydownEvent)\n }\n}\n\nexport default MediaModal\n","import UserCard from '../user_card/user_card.vue'\nimport { unseenNotificationsFromStore } from '../../services/notification_utils/notification_utils'\nimport GestureService from '../../services/gesture_service/gesture_service'\n\nconst SideDrawer = {\n props: [ 'logout' ],\n data: () => ({\n closed: true,\n closeGesture: undefined\n }),\n created () {\n this.closeGesture = GestureService.swipeGesture(GestureService.DIRECTION_LEFT, this.toggleDrawer)\n },\n components: { UserCard },\n computed: {\n currentUser () {\n return this.$store.state.users.currentUser\n },\n chat () { return this.$store.state.chat.channel.state === 'joined' },\n unseenNotifications () {\n return unseenNotificationsFromStore(this.$store)\n },\n unseenNotificationsCount () {\n return this.unseenNotifications.length\n },\n suggestionsEnabled () {\n return this.$store.state.instance.suggestionsEnabled\n },\n logo () {\n return this.$store.state.instance.logo\n },\n sitename () {\n return this.$store.state.instance.name\n },\n followRequestCount () {\n return this.$store.state.api.followRequests.length\n }\n },\n methods: {\n toggleDrawer () {\n this.closed = !this.closed\n },\n doLogout () {\n this.logout()\n this.toggleDrawer()\n },\n touchStart (e) {\n GestureService.beginSwipe(e, this.closeGesture)\n },\n touchMove (e) {\n GestureService.updateSwipe(e, this.closeGesture)\n }\n }\n}\n\nexport default SideDrawer\n","import { debounce } from 'lodash'\n\nconst MobilePostStatusButton = {\n data () {\n return {\n hidden: false,\n scrollingDown: false,\n inputActive: false,\n oldScrollPos: 0,\n amountScrolled: 0\n }\n },\n created () {\n if (this.autohideFloatingPostButton) {\n this.activateFloatingPostButtonAutohide()\n }\n window.addEventListener('resize', this.handleOSK)\n },\n destroyed () {\n if (this.autohideFloatingPostButton) {\n this.deactivateFloatingPostButtonAutohide()\n }\n window.removeEventListener('resize', this.handleOSK)\n },\n computed: {\n isLoggedIn () {\n return !!this.$store.state.users.currentUser\n },\n isHidden () {\n return this.autohideFloatingPostButton && (this.hidden || this.inputActive)\n },\n autohideFloatingPostButton () {\n return !!this.$store.state.config.autohideFloatingPostButton\n }\n },\n watch: {\n autohideFloatingPostButton: function (isEnabled) {\n if (isEnabled) {\n this.activateFloatingPostButtonAutohide()\n } else {\n this.deactivateFloatingPostButtonAutohide()\n }\n }\n },\n methods: {\n activateFloatingPostButtonAutohide () {\n window.addEventListener('scroll', this.handleScrollStart)\n window.addEventListener('scroll', this.handleScrollEnd)\n },\n deactivateFloatingPostButtonAutohide () {\n window.removeEventListener('scroll', this.handleScrollStart)\n window.removeEventListener('scroll', this.handleScrollEnd)\n },\n openPostForm () {\n this.$store.dispatch('openPostStatusModal')\n },\n handleOSK () {\n // This is a big hack: we're guessing from changed window sizes if the\n // on-screen keyboard is active or not. This is only really important\n // for phones in portrait mode and it's more important to show the button\n // in normal scenarios on all phones, than it is to hide it when the\n // keyboard is active.\n // Guesswork based on https://www.mydevice.io/#compare-devices\n\n // for example, iphone 4 and android phones from the same time period\n const smallPhone = window.innerWidth < 350\n const smallPhoneKbOpen = smallPhone && window.innerHeight < 345\n\n const biggerPhone = !smallPhone && window.innerWidth < 450\n const biggerPhoneKbOpen = biggerPhone && window.innerHeight < 560\n if (smallPhoneKbOpen || biggerPhoneKbOpen) {\n this.inputActive = true\n } else {\n this.inputActive = false\n }\n },\n handleScrollStart: debounce(function () {\n if (window.scrollY > this.oldScrollPos) {\n this.hidden = true\n } else {\n this.hidden = false\n }\n this.oldScrollPos = window.scrollY\n }, 100, { leading: true, trailing: false }),\n\n handleScrollEnd: debounce(function () {\n this.hidden = false\n this.oldScrollPos = window.scrollY\n }, 100, { leading: false, trailing: true })\n }\n}\n\nexport default MobilePostStatusButton\n","import SideDrawer from '../side_drawer/side_drawer.vue'\nimport Notifications from '../notifications/notifications.vue'\nimport { unseenNotificationsFromStore } from '../../services/notification_utils/notification_utils'\nimport GestureService from '../../services/gesture_service/gesture_service'\n\nconst MobileNav = {\n components: {\n SideDrawer,\n Notifications\n },\n data: () => ({\n notificationsCloseGesture: undefined,\n notificationsOpen: false\n }),\n created () {\n this.notificationsCloseGesture = GestureService.swipeGesture(\n GestureService.DIRECTION_RIGHT,\n this.closeMobileNotifications,\n 50\n )\n },\n computed: {\n currentUser () {\n return this.$store.state.users.currentUser\n },\n unseenNotifications () {\n return unseenNotificationsFromStore(this.$store)\n },\n unseenNotificationsCount () {\n return this.unseenNotifications.length\n },\n sitename () { return this.$store.state.instance.name }\n },\n methods: {\n toggleMobileSidebar () {\n this.$refs.sideDrawer.toggleDrawer()\n },\n openMobileNotifications () {\n this.notificationsOpen = true\n },\n closeMobileNotifications () {\n if (this.notificationsOpen) {\n // make sure to mark notifs seen only when the notifs were open and not\n // from close-calls.\n this.notificationsOpen = false\n this.markNotificationsAsSeen()\n }\n },\n notificationsTouchStart (e) {\n GestureService.beginSwipe(e, this.notificationsCloseGesture)\n },\n notificationsTouchMove (e) {\n GestureService.updateSwipe(e, this.notificationsCloseGesture)\n },\n scrollToTop () {\n window.scrollTo(0, 0)\n },\n logout () {\n this.$router.replace('/main/public')\n this.$store.dispatch('logout')\n },\n markNotificationsAsSeen () {\n this.$refs.notifications.markAsSeen()\n },\n onScroll ({ target: { scrollTop, clientHeight, scrollHeight } }) {\n if (this.$store.state.config.autoLoad && scrollTop + clientHeight >= scrollHeight) {\n this.$refs.notifications.fetchOlderNotifications()\n }\n }\n },\n watch: {\n $route () {\n // handles closing notificaitons when you press any router-link on the\n // notifications.\n this.closeMobileNotifications()\n }\n }\n}\n\nexport default MobileNav\n","\nimport Status from '../status/status.vue'\nimport List from '../list/list.vue'\nimport Checkbox from '../checkbox/checkbox.vue'\n\nconst UserReportingModal = {\n components: {\n Status,\n List,\n Checkbox\n },\n data () {\n return {\n comment: '',\n forward: false,\n statusIdsToReport: [],\n processing: false,\n error: false\n }\n },\n computed: {\n isLoggedIn () {\n return !!this.$store.state.users.currentUser\n },\n isOpen () {\n return this.isLoggedIn && this.$store.state.reports.modalActivated\n },\n userId () {\n return this.$store.state.reports.userId\n },\n user () {\n return this.$store.getters.findUser(this.userId)\n },\n remoteInstance () {\n return !this.user.is_local && this.user.screen_name.substr(this.user.screen_name.indexOf('@') + 1)\n },\n statuses () {\n return this.$store.state.reports.statuses\n }\n },\n watch: {\n userId: 'resetState'\n },\n methods: {\n resetState () {\n // Reset state\n this.comment = ''\n this.forward = false\n this.statusIdsToReport = []\n this.processing = false\n this.error = false\n },\n closeModal () {\n this.$store.dispatch('closeUserReportingModal')\n },\n reportUser () {\n this.processing = true\n this.error = false\n const params = {\n userId: this.userId,\n comment: this.comment,\n forward: this.forward,\n statusIds: this.statusIdsToReport\n }\n this.$store.state.api.backendInteractor.reportUser(params)\n .then(() => {\n this.processing = false\n this.resetState()\n this.closeModal()\n })\n .catch(() => {\n this.processing = false\n this.error = true\n })\n },\n clearError () {\n this.error = false\n },\n isChecked (statusId) {\n return this.statusIdsToReport.indexOf(statusId) !== -1\n },\n toggleStatus (checked, statusId) {\n if (checked === this.isChecked(statusId)) {\n return\n }\n\n if (checked) {\n this.statusIdsToReport.push(statusId)\n } else {\n this.statusIdsToReport.splice(this.statusIdsToReport.indexOf(statusId), 1)\n }\n },\n resize (e) {\n const target = e.target || e\n if (!(target instanceof window.Element)) { return }\n // Auto is needed to make textbox shrink when removing lines\n target.style.height = 'auto'\n target.style.height = `${target.scrollHeight}px`\n if (target.value === '') {\n target.style.height = null\n }\n }\n }\n}\n\nexport default UserReportingModal\n","import PostStatusForm from '../post_status_form/post_status_form.vue'\nimport get from 'lodash/get'\n\nconst PostStatusModal = {\n components: {\n PostStatusForm\n },\n data () {\n return {\n resettingForm: false\n }\n },\n computed: {\n isLoggedIn () {\n return !!this.$store.state.users.currentUser\n },\n modalActivated () {\n return this.$store.state.postStatus.modalActivated\n },\n isFormVisible () {\n return this.isLoggedIn && !this.resettingForm && this.modalActivated\n },\n params () {\n return this.$store.state.postStatus.params || {}\n }\n },\n watch: {\n params (newVal, oldVal) {\n if (get(newVal, 'repliedUser.id') !== get(oldVal, 'repliedUser.id')) {\n this.resettingForm = true\n this.$nextTick(() => {\n this.resettingForm = false\n })\n }\n },\n isFormVisible (val) {\n if (val) {\n this.$nextTick(() => this.$el && this.$el.querySelector('textarea').focus())\n }\n }\n },\n methods: {\n closeModal () {\n this.$store.dispatch('closePostStatusModal')\n }\n }\n}\n\nexport default PostStatusModal\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 },\n activeTab: {\n required: false,\n type: String\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","import apiService from '../api/api.service.js'\nimport timelineFetcherService from '../timeline_fetcher/timeline_fetcher.service.js'\nimport notificationsFetcher from '../notifications_fetcher/notifications_fetcher.service.js'\n\nconst backendInteractorService = credentials => {\n const fetchStatus = ({ id }) => {\n return apiService.fetchStatus({ id, credentials })\n }\n\n const fetchConversation = ({ id }) => {\n return apiService.fetchConversation({ id, credentials })\n }\n\n const fetchFriends = ({ id, maxId, sinceId, limit }) => {\n return apiService.fetchFriends({ id, maxId, sinceId, limit, credentials })\n }\n\n const exportFriends = ({ id }) => {\n return apiService.exportFriends({ id, credentials })\n }\n\n const fetchFollowers = ({ id, maxId, sinceId, limit }) => {\n return apiService.fetchFollowers({ id, maxId, sinceId, limit, credentials })\n }\n\n const fetchUser = ({ id }) => {\n return apiService.fetchUser({ id, credentials })\n }\n\n const fetchUserRelationship = ({ id }) => {\n return apiService.fetchUserRelationship({ id, credentials })\n }\n\n const followUser = (id) => {\n return apiService.followUser({ credentials, id })\n }\n\n const unfollowUser = (id) => {\n return apiService.unfollowUser({ credentials, id })\n }\n\n const blockUser = (id) => {\n return apiService.blockUser({ credentials, id })\n }\n\n const unblockUser = (id) => {\n return apiService.unblockUser({ credentials, id })\n }\n\n const approveUser = (id) => {\n return apiService.approveUser({ credentials, id })\n }\n\n const denyUser = (id) => {\n return apiService.denyUser({ credentials, id })\n }\n\n const startFetchingTimeline = ({ timeline, store, userId = false, tag }) => {\n return timelineFetcherService.startFetching({ timeline, store, credentials, userId, tag })\n }\n\n const startFetchingNotifications = ({ store }) => {\n return notificationsFetcher.startFetching({ store, credentials })\n }\n\n // eslint-disable-next-line camelcase\n const tagUser = ({ screen_name }, tag) => {\n return apiService.tagUser({ screen_name, tag, credentials })\n }\n\n // eslint-disable-next-line camelcase\n const untagUser = ({ screen_name }, tag) => {\n return apiService.untagUser({ screen_name, tag, credentials })\n }\n\n // eslint-disable-next-line camelcase\n const addRight = ({ screen_name }, right) => {\n return apiService.addRight({ screen_name, right, credentials })\n }\n\n // eslint-disable-next-line camelcase\n const deleteRight = ({ screen_name }, right) => {\n return apiService.deleteRight({ screen_name, right, credentials })\n }\n\n // eslint-disable-next-line camelcase\n const setActivationStatus = ({ screen_name }, status) => {\n return apiService.setActivationStatus({ screen_name, status, credentials })\n }\n\n // eslint-disable-next-line camelcase\n const deleteUser = ({ screen_name }) => {\n return apiService.deleteUser({ screen_name, credentials })\n }\n\n const vote = (pollId, choices) => {\n return apiService.vote({ credentials, pollId, choices })\n }\n\n const fetchPoll = (pollId) => {\n return apiService.fetchPoll({ credentials, pollId })\n }\n\n const updateNotificationSettings = ({ settings }) => {\n return apiService.updateNotificationSettings({ credentials, settings })\n }\n\n const fetchMutes = () => apiService.fetchMutes({ credentials })\n const muteUser = (id) => apiService.muteUser({ credentials, id })\n const unmuteUser = (id) => apiService.unmuteUser({ credentials, id })\n const subscribeUser = (id) => apiService.subscribeUser({ credentials, id })\n const unsubscribeUser = (id) => apiService.unsubscribeUser({ credentials, id })\n const fetchBlocks = () => apiService.fetchBlocks({ credentials })\n const fetchFollowRequests = () => apiService.fetchFollowRequests({ credentials })\n const fetchOAuthTokens = () => apiService.fetchOAuthTokens({ credentials })\n const revokeOAuthToken = (id) => apiService.revokeOAuthToken({ id, credentials })\n const fetchPinnedStatuses = (id) => apiService.fetchPinnedStatuses({ credentials, id })\n const pinOwnStatus = (id) => apiService.pinOwnStatus({ credentials, id })\n const unpinOwnStatus = (id) => apiService.unpinOwnStatus({ credentials, id })\n const muteConversation = (id) => apiService.muteConversation({ credentials, id })\n const unmuteConversation = (id) => apiService.unmuteConversation({ credentials, id })\n\n const getCaptcha = () => apiService.getCaptcha()\n const register = (params) => apiService.register({ credentials, params })\n const updateAvatar = ({ avatar }) => apiService.updateAvatar({ credentials, avatar })\n const updateBg = ({ background }) => apiService.updateBg({ credentials, background })\n const updateBanner = ({ banner }) => apiService.updateBanner({ credentials, banner })\n const updateProfile = ({ params }) => apiService.updateProfile({ credentials, params })\n\n const importBlocks = (file) => apiService.importBlocks({ file, credentials })\n const importFollows = (file) => apiService.importFollows({ file, credentials })\n\n const deleteAccount = ({ password }) => apiService.deleteAccount({ credentials, password })\n const changePassword = ({ password, newPassword, newPasswordConfirmation }) =>\n apiService.changePassword({ credentials, password, newPassword, newPasswordConfirmation })\n\n const fetchSettingsMFA = () => apiService.settingsMFA({ credentials })\n const generateMfaBackupCodes = () => apiService.generateMfaBackupCodes({ credentials })\n const mfaSetupOTP = () => apiService.mfaSetupOTP({ credentials })\n const mfaConfirmOTP = ({ password, token }) => apiService.mfaConfirmOTP({ credentials, password, token })\n const mfaDisableOTP = ({ password }) => apiService.mfaDisableOTP({ credentials, password })\n\n const fetchFavoritedByUsers = (id) => apiService.fetchFavoritedByUsers({ id })\n const fetchRebloggedByUsers = (id) => apiService.fetchRebloggedByUsers({ id })\n const reportUser = (params) => apiService.reportUser({ credentials, ...params })\n\n const favorite = (id) => apiService.favorite({ id, credentials })\n const unfavorite = (id) => apiService.unfavorite({ id, credentials })\n const retweet = (id) => apiService.retweet({ id, credentials })\n const unretweet = (id) => apiService.unretweet({ id, credentials })\n const search2 = ({ q, resolve, limit, offset, following }) =>\n apiService.search2({ credentials, q, resolve, limit, offset, following })\n const searchUsers = (query) => apiService.searchUsers({ query, credentials })\n\n const backendInteractorServiceInstance = {\n fetchStatus,\n fetchConversation,\n fetchFriends,\n exportFriends,\n fetchFollowers,\n followUser,\n unfollowUser,\n blockUser,\n unblockUser,\n fetchUser,\n fetchUserRelationship,\n verifyCredentials: apiService.verifyCredentials,\n startFetchingTimeline,\n startFetchingNotifications,\n fetchMutes,\n muteUser,\n unmuteUser,\n subscribeUser,\n unsubscribeUser,\n fetchBlocks,\n fetchOAuthTokens,\n revokeOAuthToken,\n fetchPinnedStatuses,\n pinOwnStatus,\n unpinOwnStatus,\n muteConversation,\n unmuteConversation,\n tagUser,\n untagUser,\n addRight,\n deleteRight,\n deleteUser,\n setActivationStatus,\n register,\n getCaptcha,\n updateAvatar,\n updateBg,\n updateBanner,\n updateProfile,\n importBlocks,\n importFollows,\n deleteAccount,\n changePassword,\n fetchSettingsMFA,\n generateMfaBackupCodes,\n mfaSetupOTP,\n mfaConfirmOTP,\n mfaDisableOTP,\n fetchFollowRequests,\n approveUser,\n denyUser,\n vote,\n fetchPoll,\n fetchFavoritedByUsers,\n fetchRebloggedByUsers,\n reportUser,\n favorite,\n unfavorite,\n retweet,\n unretweet,\n updateNotificationSettings,\n search2,\n searchUsers\n }\n\n return backendInteractorServiceInstance\n}\n\nexport default backendInteractorService\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","/* 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","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-618ed05f\\\",\\\"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","import { filter, sortBy } 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].filter(_ => _))\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 visibleNotificationsFromStore = (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(visibleNotificationsFromStore(store), ({ seen }) => !seen)\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!./follow_card.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./follow_card.js\"\nimport __vue_script__ from \"!!babel-loader!./follow_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-5637bff8\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./follow_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","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!./list.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!../../../node_modules/vue-loader/lib/selector?type=script&index=0!./list.vue\"\nimport __vue_script__ from \"!!babel-loader!../../../node_modules/vue-loader/lib/selector?type=script&index=0!./list.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-c1790f52\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./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:\"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 }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('video',{staticClass:\"video\",attrs:{\"src\":_vm.attachment.url,\"loop\":_vm.loopVideo,\"controls\":_vm.controls,\"playsinline\":\"\"},on:{\"loadeddata\":_vm.onVideoDataLoad}})}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","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}})],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 }","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.hidePostStatsLocal && _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.hidePostStatsLocal && _vm.status.fave_num > 0)?_c('span',[_vm._v(_vm._s(_vm.status.fave_num))]):_vm._e()])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","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.hidePostStatsLocal && _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.hidePostStatsLocal && _vm.status.repeat_num > 0)?_c('span',[_vm._v(_vm._s(_vm.status.repeat_num))]):_vm._e()]):_vm._e()}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","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 }","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 }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return (_vm.canDelete || _vm.canMute || _vm.canPin)?_c('v-popover',{staticClass:\"extra-button-popover\",attrs:{\"trigger\":\"click\",\"placement\":\"top\",\"offset\":5,\"container\":false}},[_c('div',{attrs:{\"slot\":\"popover\"},slot:\"popover\"},[_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('div',{staticClass:\"button-icon\"},[_c('i',{staticClass:\"icon-ellipsis\"})])]):_vm._e()}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","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:\"btn btn-default\",attrs:{\"title\":_vm.$t('tool_tip.media_upload')}},[(_vm.uploading)?_c('i',{staticClass:\"icon-spin4 animate-spin\"}):_vm._e(),_vm._v(\" \"),(!_vm.uploading)?_c('i',{staticClass:\"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 }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return (!_vm.showNothing)?_c('div',{staticClass:\"scope-selector\"},[(_vm.showDirect)?_c('i',{staticClass:\"icon-mail-alt\",class:_vm.css.direct,attrs:{\"title\":_vm.$t('post_status.scope.direct')},on:{\"click\":function($event){_vm.changeVis('direct')}}}):_vm._e(),_vm._v(\" \"),(_vm.showPrivate)?_c('i',{staticClass:\"icon-lock\",class:_vm.css.private,attrs:{\"title\":_vm.$t('post_status.scope.private')},on:{\"click\":function($event){_vm.changeVis('private')}}}):_vm._e(),_vm._v(\" \"),(_vm.showUnlisted)?_c('i',{staticClass:\"icon-lock-open-alt\",class:_vm.css.unlisted,attrs:{\"title\":_vm.$t('post_status.scope.unlisted')},on:{\"click\":function($event){_vm.changeVis('unlisted')}}}):_vm._e(),_vm._v(\" \"),(_vm.showPublic)?_c('i',{staticClass:\"icon-globe\",class:_vm.css.public,attrs:{\"title\":_vm.$t('post_status.scope.public')},on:{\"click\":function($event){_vm.changeVis('public')}}}):_vm._e()]):_vm._e()}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","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.scrolledGroup}},_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}})])})],2)}),0),_vm._v(\" \"),_c('div',{staticClass:\"keep-open\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.keepOpen),expression:\"keepOpen\"}],attrs:{\"id\":_vm.labelKey + 'keep-open',\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.keepOpen)?_vm._i(_vm.keepOpen,null)>-1:(_vm.keepOpen)},on:{\"change\":function($event){var $$a=_vm.keepOpen,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.keepOpen=$$a.concat([$$v]))}else{$$i>-1&&(_vm.keepOpen=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.keepOpen=$$c}}}}),_vm._v(\" \"),_c('label',{staticClass:\"keep-open-label\",attrs:{\"for\":_vm.labelKey + 'keep-open'}},[_c('div',{staticClass:\"keep-open-label-text\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('emoji.keep_open'))+\"\\n \")])])])]),_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 }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{directives:[{name:\"click-outside\",rawName:\"v-click-outside\",value:(_vm.onClickOutside),expression:\"onClickOutside\"}],staticClass:\"emoji-input\"},[_vm._t(\"default\"),_vm._v(\" \"),(_vm.enableEmojiPicker)?[(!_vm.hideEmojiButton)?_c('div',{staticClass:\"emoji-picker-icon\",on:{\"click\":function($event){$event.preventDefault();return _vm.togglePicker($event)}}},[_c('i',{staticClass:\"icon-smile\"})]):_vm._e(),_vm._v(\" \"),(_vm.enableEmojiPicker)?_c('EmojiPicker',{ref:\"picker\",staticClass:\"emoji-picker-panel\",class:{ hide: !_vm.showPicker },attrs:{\"enable-sticker-picker\":_vm.enableStickerPicker},on:{\"emoji\":_vm.insert,\"sticker-uploaded\":_vm.onStickerUploaded,\"sticker-upload-failed\":_vm.onStickerUploadFailed}}):_vm._e()]:_vm._e(),_vm._v(\" \"),_c('div',{ref:\"panel\",staticClass:\"autocomplete-panel\",class:{ hide: !_vm.showSuggestions }},[_c('div',{staticClass:\"autocomplete-panel-body\"},_vm._l((_vm.suggestions),function(suggestion,index){return _c('div',{key:index,staticClass:\"autocomplete-item\",class:{ highlighted: suggestion.highlighted },on:{\"click\":function($event){$event.stopPropagation();$event.preventDefault();_vm.onClick($event, suggestion)}}},[_c('span',{staticClass:\"image\"},[(suggestion.img)?_c('img',{attrs:{\"src\":suggestion.img}}):_c('span',[_vm._v(_vm._s(suggestion.replacement))])]),_vm._v(\" \"),_c('div',{staticClass:\"label\"},[_c('span',{staticClass:\"displayText\"},[_vm._v(_vm._s(suggestion.displayText))]),_vm._v(\" \"),_c('span',{staticClass:\"detailText\"},[_vm._v(_vm._s(suggestion.detailText))])])])}),0)])],2)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","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 }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{ref:\"root\",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',{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('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.newStatus.nsfw),expression:\"newStatus.nsfw\"}],attrs:{\"id\":\"filesSensitive\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.newStatus.nsfw)?_vm._i(_vm.newStatus.nsfw,null)>-1:(_vm.newStatus.nsfw)},on:{\"change\":function($event){var $$a=_vm.newStatus.nsfw,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.$set(_vm.newStatus, \"nsfw\", $$a.concat([$$v])))}else{$$i>-1&&(_vm.$set(_vm.newStatus, \"nsfw\", $$a.slice(0,$$i).concat($$a.slice($$i+1))))}}else{_vm.$set(_vm.newStatus, \"nsfw\", $$c)}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"filesSensitive\"}},[_vm._v(_vm._s(_vm.$t('post_status.attachments_sensitive')))])]):_vm._e()],1)])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","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 }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"remote-follow\"},[_c('form',{attrs:{\"method\":\"POST\",\"action\":_vm.subscribeUrl}},[_c('input',{attrs:{\"type\":\"hidden\",\"name\":\"nickname\"},domProps:{\"value\":_vm.user.screen_name}}),_vm._v(\" \"),_c('input',{attrs:{\"type\":\"hidden\",\"name\":\"profile\",\"value\":\"\"}}),_vm._v(\" \"),_c('button',{staticClass:\"remote-button\",attrs:{\"click\":\"submit\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.remote_follow'))+\"\\n \")])])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","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 }","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 }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('v-popover',{staticClass:\"moderation-tools-popover\",attrs:{\"trigger\":\"click\",\"container\":false,\"placement\":\"bottom-end\",\"offset\":5},on:{\"show\":function($event){_vm.showDropDown = true},\"hide\":function($event){_vm.showDropDown = false}}},[_c('div',{attrs:{\"slot\":\"popover\"},slot:\"popover\"},[_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:{ pressed: _vm.showDropDown }},[_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 }","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()],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.hideUserStatsLocal && !_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\"},[(!_vm.user.following)?_c('div',[_c('button',{staticClass:\"btn btn-default btn-block\",attrs:{\"disabled\":_vm.followRequestInProgress,\"title\":_vm.user.requested ? _vm.$t('user_card.follow_again') : ''},on:{\"click\":_vm.followUser}},[(_vm.followRequestInProgress)?[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.follow_progress'))+\"\\n \")]:(_vm.user.requested)?[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.follow_sent'))+\"\\n \")]:[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.follow'))+\"\\n \")]],2)]):(_vm.followRequestInProgress)?_c('div',[_c('button',{staticClass:\"btn btn-default btn-block pressed\",attrs:{\"disabled\":\"\",\"title\":_vm.$t('user_card.follow_unfollow')},on:{\"click\":_vm.unfollowUser}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.follow_progress'))+\"\\n \")])]):_c('div',{staticClass:\"btn-group\"},[_c('button',{staticClass:\"btn btn-default pressed\",attrs:{\"title\":_vm.$t('user_card.follow_unfollow')},on:{\"click\":_vm.unfollowUser}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.following'))+\"\\n \")]),_vm._v(\" \"),(!_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 pressed\",attrs:{\"click\":_vm.unsubscribeUser,\"title\":_vm.$t('user_card.unsubscribe')}},[_c('i',{staticClass:\"icon-bell-ringing-o\"})])],1),_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(\" \"),_c('div',[(_vm.user.muted)?_c('button',{staticClass:\"btn btn-default btn-block pressed\",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',[(_vm.user.statusnet_blocking)?_c('button',{staticClass:\"btn btn-default btn-block pressed\",on:{\"click\":_vm.unblockUser}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.blocked'))+\"\\n \")]):_c('button',{staticClass:\"btn btn-default btn-block\",on:{\"click\":_vm.blockUser}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.block'))+\"\\n \")])]),_vm._v(\" \"),_c('div',[_c('button',{staticClass:\"btn btn-default btn-block\",on:{\"click\":_vm.reportUser}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.report'))+\"\\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.hideUserStatsLocal && _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.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.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 }","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.rowHeight(row.length))},_vm._l((row),function(attachment){return _c('attachment',{key:attachment.id,attrs:{\"set-media\":_vm.setMedia,\"nsfw\":_vm.nsfw,\"attachment\":attachment,\"allow-play\":false}})}),1)}),0)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","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 }","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 }","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\"},[_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)},\"mouseenter\":function($event){$event.preventDefault();$event.stopPropagation();_vm.replyEnter(_vm.status.in_reply_to_status_id, $event)},\"mouseleave\":function($event){$event.preventDefault();$event.stopPropagation();_vm.replyLeave()}}},[(!_vm.isPreview)?_c('i',{staticClass:\"button-icon icon-reply\"}):_vm._e(),_vm._v(\" \"),_c('span',{staticClass:\"faint-link 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)?_c('div',{staticClass:\"replies\"},[(_vm.replies && _vm.replies.length)?_c('span',{staticClass:\"faint\"},[_vm._v(_vm._s(_vm.$t('status.replies_list')))]):_vm._e(),_vm._v(\" \"),(_vm.replies)?_vm._l((_vm.replies),function(reply){return _c('span',{key:reply.id,staticClass:\"reply-link faint\"},[_c('a',{attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();_vm.gotoOriginal(reply.id)},\"mouseenter\":function($event){_vm.replyEnter(reply.id, $event)},\"mouseout\":function($event){_vm.replyLeave()}}},[_vm._v(_vm._s(reply.name))])])}):_vm._e()],2):_vm._e()])]):_vm._e(),_vm._v(\" \"),(_vm.showPreview)?_c('div',{staticClass:\"status-preview-container\"},[(_vm.preview)?_c('status',{staticClass:\"status-preview\",attrs:{\"is-preview\":true,\"statusoid\":_vm.preview,\"compact\":true}}):_c('div',{staticClass:\"status-preview status-preview-loading\"},[_c('i',{staticClass:\"icon-spin4 animate-spin\"})])],1):_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(_vm._s(_vm.$t(\"general.show_more\")))]):_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.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('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 }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"timeline panel-default\",class:[_vm.isExpanded ? 'panel' : 'panel-disabled']},[(_vm.isExpanded)?_c('div',{staticClass:\"panel-heading conversation-heading\"},[_c('span',{staticClass:\"title\"},[_vm._v(\" \"+_vm._s(_vm.$t('timeline.conversation'))+\" \")]),_vm._v(\" \"),(_vm.collapsable)?_c('span',[_c('a',{attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();return _vm.toggleExpanded($event)}}},[_vm._v(_vm._s(_vm.$t('timeline.collapse')))])]):_vm._e()]):_vm._e(),_vm._v(\" \"),_vm._l((_vm.conversation),function(status){return _c('status',{key:status.id,staticClass:\"status-fadein panel-body\",attrs:{\"inline-expanded\":_vm.collapsable && _vm.isExpanded,\"statusoid\":status,\"expandable\":!_vm.isExpanded,\"show-pinned\":_vm.pinnedStatusIdsObject && _vm.pinnedStatusIdsObject[status.id],\"focused\":_vm.focused(status.id),\"in-conversation\":_vm.isExpanded,\"highlight\":_vm.getHighlight(),\"replies\":_vm.getReplies(status.id),\"in-profile\":_vm.inProfile},on:{\"goto\":_vm.setHighlight,\"toggleExpanded\":_vm.toggleExpanded}})})],2)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{class:_vm.classes.root},[_c('div',{class:_vm.classes.header},[_c('div',{staticClass:\"title\"},[_vm._v(\"\\n \"+_vm._s(_vm.title)+\"\\n \")]),_vm._v(\" \"),(_vm.timelineError)?_c('div',{staticClass:\"loadmore-error alert error\",on:{\"click\":function($event){$event.preventDefault();}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('timeline.error_fetching'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(_vm.timeline.newStatusCount > 0 && !_vm.timelineError)?_c('button',{staticClass:\"loadmore-button\",on:{\"click\":function($event){$event.preventDefault();return _vm.showNewStatuses($event)}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('timeline.show_new'))+_vm._s(_vm.newStatusCountStr)+\"\\n \")]):_vm._e(),_vm._v(\" \"),(!_vm.timeline.newStatusCount > 0 && !_vm.timelineError)?_c('div',{staticClass:\"loadmore-text faint\",on:{\"click\":function($event){$event.preventDefault();}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('timeline.up_to_date'))+\"\\n \")]):_vm._e()]),_vm._v(\" \"),_c('div',{class:_vm.classes.body},[_c('div',{staticClass:\"timeline\"},[_vm._l((_vm.pinnedStatusIds),function(statusId){return [(_vm.timeline.statusesObject[statusId])?_c('conversation',{key:statusId + '-pinned',staticClass:\"status-fadein\",attrs:{\"status-id\":statusId,\"collapsable\":true,\"pinned-status-ids-object\":_vm.pinnedStatusIdsObject,\"in-profile\":_vm.inProfile}}):_vm._e()]}),_vm._v(\" \"),_vm._l((_vm.timeline.visibleStatuses),function(status){return [(!_vm.excludedStatusIdsObject[status.id])?_c('conversation',{key:status.id,staticClass:\"status-fadein\",attrs:{\"status-id\":status.id,\"collapsable\":true,\"in-profile\":_vm.inProfile}}):_vm._e()]})],2)]),_vm._v(\" \"),_c('div',{class:_vm.classes.footer},[(_vm.count===0)?_c('div',{staticClass:\"new-status-notification text-center panel-footer faint\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('timeline.no_statuses'))+\"\\n \")]):(_vm.bottomedOut)?_c('div',{staticClass:\"new-status-notification text-center panel-footer faint\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('timeline.no_more_statuses'))+\"\\n \")]):(!_vm.timeline.loading)?_c('a',{attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();_vm.fetchOlderStatuses()}}},[_c('div',{staticClass:\"new-status-notification text-center panel-footer\"},[_vm._v(_vm._s(_vm.$t('timeline.load_older')))])]):_c('div',{staticClass:\"new-status-notification text-center panel-footer\"},[_c('i',{staticClass:\"icon-spin3 animate-spin\"})])])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('Timeline',{attrs:{\"title\":_vm.$t('nav.public_tl'),\"timeline\":_vm.timeline,\"timeline-name\":'public'}})}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('Timeline',{attrs:{\"title\":_vm.$t('nav.twkn'),\"timeline\":_vm.timeline,\"timeline-name\":'publicAndExternal'}})}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('Timeline',{attrs:{\"title\":_vm.$t('nav.timeline'),\"timeline\":_vm.timeline,\"timeline-name\":'friends'}})}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('Timeline',{attrs:{\"title\":_vm.tag,\"timeline\":_vm.timeline,\"timeline-name\":'tag',\"tag\":_vm.tag}})}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('conversation',{attrs:{\"collapsable\":false,\"is-page\":\"true\",\"status-id\":_vm.statusId}})}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return (_vm.notification.type === 'mention')?_c('status',{attrs:{\"compact\":true,\"statusoid\":_vm.notification.status}}):_c('div',[(_vm.needMute && !_vm.unmuted)?_c('div',{staticClass:\"container muted\"},[_c('small',[_c('router-link',{attrs:{\"to\":_vm.userProfileLink}},[_vm._v(\"\\n \"+_vm._s(_vm.notification.from_profile.screen_name)+\"\\n \")])],1),_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\"})])]):_c('div',{staticClass:\"non-mention\",class:[_vm.userClass, { highlighted: _vm.userStyle }],style:([ _vm.userStyle ])},[_c('a',{staticClass:\"avatar-container\",attrs:{\"href\":_vm.notification.from_profile.statusnet_profile_url},on:{\"!click\":function($event){$event.stopPropagation();$event.preventDefault();return _vm.toggleUserExpanded($event)}}},[_c('UserAvatar',{attrs:{\"compact\":true,\"better-shadow\":_vm.betterShadow,\"user\":_vm.notification.from_profile}})],1),_vm._v(\" \"),_c('div',{staticClass:\"notification-right\"},[(_vm.userExpanded)?_c('UserCard',{attrs:{\"user\":_vm.getUser(_vm.notification),\"rounded\":true,\"bordered\":true}}):_vm._e(),_vm._v(\" \"),_c('span',{staticClass:\"notification-details\"},[_c('div',{staticClass:\"name-and-action\"},[(!!_vm.notification.from_profile.name_html)?_c('span',{staticClass:\"username\",attrs:{\"title\":'@'+_vm.notification.from_profile.screen_name},domProps:{\"innerHTML\":_vm._s(_vm.notification.from_profile.name_html)}}):_c('span',{staticClass:\"username\",attrs:{\"title\":'@'+_vm.notification.from_profile.screen_name}},[_vm._v(_vm._s(_vm.notification.from_profile.name))]),_vm._v(\" \"),(_vm.notification.type === 'like')?_c('span',[_c('i',{staticClass:\"fa icon-star lit\"}),_vm._v(\" \"),_c('small',[_vm._v(_vm._s(_vm.$t('notifications.favorited_you')))])]):_vm._e(),_vm._v(\" \"),(_vm.notification.type === 'repeat')?_c('span',[_c('i',{staticClass:\"fa icon-retweet lit\",attrs:{\"title\":_vm.$t('tool_tip.repeat')}}),_vm._v(\" \"),_c('small',[_vm._v(_vm._s(_vm.$t('notifications.repeated_you')))])]):_vm._e(),_vm._v(\" \"),(_vm.notification.type === 'follow')?_c('span',[_c('i',{staticClass:\"fa icon-user-plus lit\"}),_vm._v(\" \"),_c('small',[_vm._v(_vm._s(_vm.$t('notifications.followed_you')))])]):_vm._e()]),_vm._v(\" \"),(_vm.notification.type === 'follow')?_c('div',{staticClass:\"timeago\"},[_c('span',{staticClass:\"faint\"},[_c('Timeago',{attrs:{\"time\":_vm.notification.created_at,\"auto-update\":240}})],1)]):_c('div',{staticClass:\"timeago\"},[(_vm.notification.status)?_c('router-link',{staticClass:\"faint-link\",attrs:{\"to\":{ name: 'conversation', params: { id: _vm.notification.status.id } }}},[_c('Timeago',{attrs:{\"time\":_vm.notification.created_at,\"auto-update\":240}})],1):_vm._e()],1),_vm._v(\" \"),(_vm.needMute)?_c('a',{attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();return _vm.toggleMute($event)}}},[_c('i',{staticClass:\"button-icon icon-eye-off\"})]):_vm._e()]),_vm._v(\" \"),(_vm.notification.type === 'follow')?_c('div',{staticClass:\"follow-text\"},[_c('router-link',{attrs:{\"to\":_vm.userProfileLink}},[_vm._v(\"\\n @\"+_vm._s(_vm.notification.from_profile.screen_name)+\"\\n \")])],1):[_c('status',{staticClass:\"faint\",attrs:{\"compact\":true,\"statusoid\":_vm.notification.action,\"no-heading\":true}})]],2)])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"notifications\",class:{ minimal: _vm.minimalMode }},[_c('div',{class:_vm.mainClass},[(!_vm.noHeading)?_c('div',{staticClass:\"panel-heading\"},[_c('div',{staticClass:\"title\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('notifications.notifications'))+\"\\n \"),(_vm.unseenCount)?_c('span',{staticClass:\"badge badge-notification unseen-count\"},[_vm._v(_vm._s(_vm.unseenCount))]):_vm._e()]),_vm._v(\" \"),(_vm.error)?_c('div',{staticClass:\"loadmore-error alert error\",on:{\"click\":function($event){$event.preventDefault();}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('timeline.error_fetching'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(_vm.unseenCount)?_c('button',{staticClass:\"read-button\",on:{\"click\":function($event){$event.preventDefault();return _vm.markAsSeen($event)}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('notifications.read'))+\"\\n \")]):_vm._e()]):_vm._e(),_vm._v(\" \"),_c('div',{staticClass:\"panel-body\"},_vm._l((_vm.visibleNotifications),function(notification){return _c('div',{key:notification.id,staticClass:\"notification\",class:{\"unseen\": !_vm.minimalMode && !notification.seen}},[_c('div',{staticClass:\"notification-overlay\"}),_vm._v(\" \"),_c('notification',{attrs:{\"notification\":notification}})],1)}),0),_vm._v(\" \"),_c('div',{staticClass:\"panel-footer\"},[(_vm.bottomedOut)?_c('div',{staticClass:\"new-status-notification text-center panel-footer faint\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('notifications.no_more_notifications'))+\"\\n \")]):(!_vm.loading)?_c('a',{attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();_vm.fetchOlderNotifications()}}},[_c('div',{staticClass:\"new-status-notification text-center panel-footer\"},[_vm._v(\"\\n \"+_vm._s(_vm.minimalMode ? _vm.$t('interactions.load_older') : _vm.$t('notifications.load_older'))+\"\\n \")])]):_c('div',{staticClass:\"new-status-notification text-center panel-footer\"},[_c('i',{staticClass:\"icon-spin3 animate-spin\"})])])])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"panel panel-default\"},[_c('div',{staticClass:\"panel-heading\"},[_c('div',{staticClass:\"title\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"nav.interactions\"))+\"\\n \")])]),_vm._v(\" \"),_c('tab-switcher',{ref:\"tabSwitcher\",attrs:{\"on-switch\":_vm.onModeSwitch}},[_c('span',{key:\"mentions\",attrs:{\"label\":_vm.$t('nav.mentions')}}),_vm._v(\" \"),_c('span',{key:\"likes+repeats\",attrs:{\"label\":_vm.$t('interactions.favs_repeats')}}),_vm._v(\" \"),_c('span',{key:\"follows\",attrs:{\"label\":_vm.$t('interactions.follows')}})]),_vm._v(\" \"),_c('Notifications',{ref:\"notifications\",attrs:{\"no-heading\":true,\"minimal-mode\":true,\"filter-mode\":_vm.filterMode}})],1)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('Timeline',{attrs:{\"title\":_vm.$t('nav.dms'),\"timeline\":_vm.timeline,\"timeline-name\":'dms'}})}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"basic-user-card\"},[_c('router-link',{attrs:{\"to\":_vm.userProfileLink(_vm.user)}},[_c('UserAvatar',{staticClass:\"avatar\",attrs:{\"user\":_vm.user},nativeOn:{\"click\":function($event){$event.preventDefault();return _vm.toggleUserExpanded($event)}}})],1),_vm._v(\" \"),(_vm.userExpanded)?_c('div',{staticClass:\"basic-user-card-expanded-content\"},[_c('UserCard',{attrs:{\"user\":_vm.user,\"rounded\":true,\"bordered\":true}})],1):_c('div',{staticClass:\"basic-user-card-collapsed-content\"},[_c('div',{staticClass:\"basic-user-card-user-name\",attrs:{\"title\":_vm.user.name}},[(_vm.user.name_html)?_c('span',{staticClass:\"basic-user-card-user-name-value\",domProps:{\"innerHTML\":_vm._s(_vm.user.name_html)}}):_c('span',{staticClass:\"basic-user-card-user-name-value\"},[_vm._v(_vm._s(_vm.user.name))])]),_vm._v(\" \"),_c('div',[_c('router-link',{staticClass:\"basic-user-card-screen-name\",attrs:{\"to\":_vm.userProfileLink(_vm.user)}},[_vm._v(\"\\n @\"+_vm._s(_vm.user.screen_name)+\"\\n \")])],1),_vm._v(\" \"),_vm._t(\"default\")],2)],1)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('basic-user-card',{attrs:{\"user\":_vm.user}},[_c('div',{staticClass:\"follow-card-content-container\"},[(!_vm.noFollowsYou && _vm.user.follows_you)?_c('span',{staticClass:\"faint\"},[_vm._v(\"\\n \"+_vm._s(_vm.isMe ? _vm.$t('user_card.its_you') : _vm.$t('user_card.follows_you'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(!_vm.loggedIn)?[(!_vm.user.following)?_c('div',{staticClass:\"follow-card-follow-button\"},[_c('RemoteFollow',{attrs:{\"user\":_vm.user}})],1):_vm._e()]:[(!_vm.user.following)?_c('button',{staticClass:\"btn btn-default follow-card-follow-button\",attrs:{\"disabled\":_vm.inProgress,\"title\":_vm.requestSent ? _vm.$t('user_card.follow_again') : ''},on:{\"click\":_vm.followUser}},[(_vm.inProgress)?[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.follow_progress'))+\"\\n \")]:(_vm.requestSent)?[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.follow_sent'))+\"\\n \")]:[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.follow'))+\"\\n \")]],2):_c('button',{staticClass:\"btn btn-default follow-card-follow-button pressed\",attrs:{\"disabled\":_vm.inProgress},on:{\"click\":_vm.unfollowUser}},[(_vm.inProgress)?[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.follow_progress'))+\"\\n \")]:[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.follow_unfollow'))+\"\\n \")]],2)]],2)])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"list\"},[_vm._l((_vm.items),function(item){return _c('div',{key:_vm.getKey(item),staticClass:\"list-item\"},[_vm._t(\"item\",null,{item:item})],2)}),_vm._v(\" \"),(_vm.items.length === 0 && !!_vm.$slots.empty)?_c('div',{staticClass:\"list-empty-content faint\"},[_vm._t(\"empty\")],2):_vm._e()],2)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[(_vm.user)?_c('div',{staticClass:\"user-profile panel panel-default\"},[_c('UserCard',{attrs:{\"user\":_vm.user,\"switcher\":true,\"selected\":_vm.timeline.viewing,\"allow-zooming-avatar\":true,\"rounded\":\"top\"}}),_vm._v(\" \"),_c('tab-switcher',{attrs:{\"active-tab\":_vm.tab,\"render-only-focused\":true,\"on-switch\":_vm.onTabSwitch}},[_c('Timeline',{key:\"statuses\",attrs:{\"label\":_vm.$t('user_card.statuses'),\"count\":_vm.user.statuses_count,\"embedded\":true,\"title\":_vm.$t('user_profile.timeline_title'),\"timeline\":_vm.timeline,\"timeline-name\":\"user\",\"user-id\":_vm.userId,\"pinned-status-ids\":_vm.user.pinnedStatusIds,\"in-profile\":true}}),_vm._v(\" \"),(_vm.followsTabVisible)?_c('div',{key:\"followees\",attrs:{\"label\":_vm.$t('user_card.followees'),\"disabled\":!_vm.user.friends_count}},[_c('FriendList',{attrs:{\"user-id\":_vm.userId},scopedSlots:_vm._u([{key:\"item\",fn:function(ref){\nvar item = ref.item;\nreturn [_c('FollowCard',{attrs:{\"user\":item}})]}}])})],1):_vm._e(),_vm._v(\" \"),(_vm.followersTabVisible)?_c('div',{key:\"followers\",attrs:{\"label\":_vm.$t('user_card.followers'),\"disabled\":!_vm.user.followers_count}},[_c('FollowerList',{attrs:{\"user-id\":_vm.userId},scopedSlots:_vm._u([{key:\"item\",fn:function(ref){\nvar item = ref.item;\nreturn [_c('FollowCard',{attrs:{\"user\":item,\"no-follows-you\":_vm.isUs}})]}}])})],1):_vm._e(),_vm._v(\" \"),_c('Timeline',{key:\"media\",attrs:{\"label\":_vm.$t('user_card.media'),\"disabled\":!_vm.media.visibleStatuses.length,\"embedded\":true,\"title\":_vm.$t('user_card.media'),\"timeline-name\":\"media\",\"timeline\":_vm.media,\"user-id\":_vm.userId,\"in-profile\":true}}),_vm._v(\" \"),(_vm.isUs)?_c('Timeline',{key:\"favorites\",attrs:{\"label\":_vm.$t('user_card.favorites'),\"disabled\":!_vm.favorites.visibleStatuses.length,\"embedded\":true,\"title\":_vm.$t('user_card.favorites'),\"timeline-name\":\"favorites\",\"timeline\":_vm.favorites,\"in-profile\":true}}):_vm._e()],1)],1):_c('div',{staticClass:\"panel user-profile-placeholder\"},[_c('div',{staticClass:\"panel-heading\"},[_c('div',{staticClass:\"title\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.profile_tab'))+\"\\n \")])]),_vm._v(\" \"),_c('div',{staticClass:\"panel-body\"},[(_vm.error)?_c('span',[_vm._v(_vm._s(_vm.error))]):_c('i',{staticClass:\"icon-spin3 animate-spin\"})])])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"panel panel-default\"},[_c('div',{staticClass:\"panel-heading\"},[_c('div',{staticClass:\"title\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('nav.search'))+\"\\n \")])]),_vm._v(\" \"),_c('div',{staticClass:\"search-input-container\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.searchTerm),expression:\"searchTerm\"}],ref:\"searchInput\",staticClass:\"search-input\",attrs:{\"placeholder\":_vm.$t('nav.search')},domProps:{\"value\":(_vm.searchTerm)},on:{\"keyup\":function($event){if(!('button' in $event)&&_vm._k($event.keyCode,\"enter\",13,$event.key,\"Enter\")){ return null; }_vm.newQuery(_vm.searchTerm)},\"input\":function($event){if($event.target.composing){ return; }_vm.searchTerm=$event.target.value}}}),_vm._v(\" \"),_c('button',{staticClass:\"btn search-button\",on:{\"click\":function($event){_vm.newQuery(_vm.searchTerm)}}},[_c('i',{staticClass:\"icon-search\"})])]),_vm._v(\" \"),(_vm.loading)?_c('div',{staticClass:\"text-center loading-icon\"},[_c('i',{staticClass:\"icon-spin3 animate-spin\"})]):(_vm.loaded)?_c('div',[_c('div',{staticClass:\"search-nav-heading\"},[_c('tab-switcher',{ref:\"tabSwitcher\",attrs:{\"on-switch\":_vm.onResultTabSwitch,\"active-tab\":_vm.currenResultTab}},[_c('span',{key:\"statuses\",attrs:{\"label\":_vm.$t('user_card.statuses') + _vm.resultCount('visibleStatuses')}}),_vm._v(\" \"),_c('span',{key:\"people\",attrs:{\"label\":_vm.$t('search.people') + _vm.resultCount('users')}}),_vm._v(\" \"),_c('span',{key:\"hashtags\",attrs:{\"label\":_vm.$t('search.hashtags') + _vm.resultCount('hashtags')}})])],1)]):_vm._e(),_vm._v(\" \"),_c('div',{staticClass:\"panel-body\"},[(_vm.currenResultTab === 'statuses')?_c('div',[(_vm.visibleStatuses.length === 0 && !_vm.loading && _vm.loaded)?_c('div',{staticClass:\"search-result-heading\"},[_c('h4',[_vm._v(_vm._s(_vm.$t('search.no_results')))])]):_vm._e(),_vm._v(\" \"),_vm._l((_vm.visibleStatuses),function(status){return _c('Status',{key:status.id,staticClass:\"search-result\",attrs:{\"collapsable\":false,\"expandable\":false,\"compact\":false,\"statusoid\":status,\"no-heading\":false}})})],2):(_vm.currenResultTab === 'people')?_c('div',[(_vm.users.length === 0 && !_vm.loading && _vm.loaded)?_c('div',{staticClass:\"search-result-heading\"},[_c('h4',[_vm._v(_vm._s(_vm.$t('search.no_results')))])]):_vm._e(),_vm._v(\" \"),_vm._l((_vm.users),function(user){return _c('FollowCard',{key:user.id,staticClass:\"list-item search-result\",attrs:{\"user\":user}})})],2):(_vm.currenResultTab === 'hashtags')?_c('div',[(_vm.hashtags.length === 0 && !_vm.loading && _vm.loaded)?_c('div',{staticClass:\"search-result-heading\"},[_c('h4',[_vm._v(_vm._s(_vm.$t('search.no_results')))])]):_vm._e(),_vm._v(\" \"),_vm._l((_vm.hashtags),function(hashtag){return _c('div',{key:hashtag.url,staticClass:\"status trend search-result\"},[_c('div',{staticClass:\"hashtag\"},[_c('router-link',{attrs:{\"to\":{ name: 'tag-timeline', params: { tag: hashtag.name } }}},[_vm._v(\"\\n #\"+_vm._s(hashtag.name)+\"\\n \")]),_vm._v(\" \"),(_vm.lastHistoryRecord(hashtag))?_c('div',[(_vm.lastHistoryRecord(hashtag).accounts == 1)?_c('span',[_vm._v(\"\\n \"+_vm._s(_vm.$t('search.person_talking', { count: _vm.lastHistoryRecord(hashtag).accounts }))+\"\\n \")]):_c('span',[_vm._v(\"\\n \"+_vm._s(_vm.$t('search.people_talking', { count: _vm.lastHistoryRecord(hashtag).accounts }))+\"\\n \")])]):_vm._e()],1),_vm._v(\" \"),(_vm.lastHistoryRecord(hashtag))?_c('div',{staticClass:\"count\"},[_vm._v(\"\\n \"+_vm._s(_vm.lastHistoryRecord(hashtag).uses)+\"\\n \")]):_vm._e()])})],2):_vm._e()]),_vm._v(\" \"),_c('div',{staticClass:\"search-result-footer text-center panel-footer faint\"})])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"color-control style-control\",class:{ disabled: !_vm.present || _vm.disabled }},[_c('label',{staticClass:\"label\",attrs:{\"for\":_vm.name}},[_vm._v(\"\\n \"+_vm._s(_vm.label)+\"\\n \")]),_vm._v(\" \"),(typeof _vm.fallback !== 'undefined')?_c('input',{staticClass:\"opt exlcude-disabled\",attrs:{\"id\":_vm.name + '-o',\"type\":\"checkbox\"},domProps:{\"checked\":_vm.present},on:{\"input\":function($event){_vm.$emit('input', typeof _vm.value === 'undefined' ? _vm.fallback : undefined)}}}):_vm._e(),_vm._v(\" \"),(typeof _vm.fallback !== 'undefined')?_c('label',{staticClass:\"opt-l\",attrs:{\"for\":_vm.name + '-o'}}):_vm._e(),_vm._v(\" \"),_c('input',{staticClass:\"color-input\",attrs:{\"id\":_vm.name,\"type\":\"color\",\"disabled\":!_vm.present || _vm.disabled},domProps:{\"value\":_vm.value || _vm.fallback},on:{\"input\":function($event){_vm.$emit('input', $event.target.value)}}}),_vm._v(\" \"),_c('input',{staticClass:\"text-input\",attrs:{\"id\":_vm.name + '-t',\"type\":\"text\",\"disabled\":!_vm.present || _vm.disabled},domProps:{\"value\":_vm.value || _vm.fallback},on:{\"input\":function($event){_vm.$emit('input', $event.target.value)}}})])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"range-control style-control\",class:{ disabled: !_vm.present || _vm.disabled }},[_c('label',{staticClass:\"label\",attrs:{\"for\":_vm.name}},[_vm._v(\"\\n \"+_vm._s(_vm.label)+\"\\n \")]),_vm._v(\" \"),(typeof _vm.fallback !== 'undefined')?_c('input',{staticClass:\"opt exclude-disabled\",attrs:{\"id\":_vm.name + '-o',\"type\":\"checkbox\"},domProps:{\"checked\":_vm.present},on:{\"input\":function($event){_vm.$emit('input', !_vm.present ? _vm.fallback : undefined)}}}):_vm._e(),_vm._v(\" \"),(typeof _vm.fallback !== 'undefined')?_c('label',{staticClass:\"opt-l\",attrs:{\"for\":_vm.name + '-o'}}):_vm._e(),_vm._v(\" \"),_c('input',{staticClass:\"input-number\",attrs:{\"id\":_vm.name,\"type\":\"range\",\"disabled\":!_vm.present || _vm.disabled,\"max\":_vm.max || _vm.hardMax || 100,\"min\":_vm.min || _vm.hardMin || 0,\"step\":_vm.step || 1},domProps:{\"value\":_vm.value || _vm.fallback},on:{\"input\":function($event){_vm.$emit('input', $event.target.value)}}}),_vm._v(\" \"),_c('input',{staticClass:\"input-number\",attrs:{\"id\":_vm.name,\"type\":\"number\",\"disabled\":!_vm.present || _vm.disabled,\"max\":_vm.hardMax,\"min\":_vm.hardMin,\"step\":_vm.step || 1},domProps:{\"value\":_vm.value || _vm.fallback},on:{\"input\":function($event){_vm.$emit('input', $event.target.value)}}})])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"opacity-control style-control\",class:{ disabled: !_vm.present || _vm.disabled }},[_c('label',{staticClass:\"label\",attrs:{\"for\":_vm.name}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.style.common.opacity'))+\"\\n \")]),_vm._v(\" \"),(typeof _vm.fallback !== 'undefined')?_c('input',{staticClass:\"opt exclude-disabled\",attrs:{\"id\":_vm.name + '-o',\"type\":\"checkbox\"},domProps:{\"checked\":_vm.present},on:{\"input\":function($event){_vm.$emit('input', !_vm.present ? _vm.fallback : undefined)}}}):_vm._e(),_vm._v(\" \"),(typeof _vm.fallback !== 'undefined')?_c('label',{staticClass:\"opt-l\",attrs:{\"for\":_vm.name + '-o'}}):_vm._e(),_vm._v(\" \"),_c('input',{staticClass:\"input-number\",attrs:{\"id\":_vm.name,\"type\":\"number\",\"disabled\":!_vm.present || _vm.disabled,\"max\":\"1\",\"min\":\"0\",\"step\":\".05\"},domProps:{\"value\":_vm.value || _vm.fallback},on:{\"input\":function($event){_vm.$emit('input', $event.target.value)}}})])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"shadow-control\",class:{ disabled: !_vm.present }},[_c('div',{staticClass:\"shadow-preview-container\"},[_c('div',{staticClass:\"y-shift-control\",attrs:{\"disabled\":!_vm.present}},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.selected.y),expression:\"selected.y\"}],staticClass:\"input-number\",attrs:{\"disabled\":!_vm.present,\"type\":\"number\"},domProps:{\"value\":(_vm.selected.y)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.selected, \"y\", $event.target.value)}}}),_vm._v(\" \"),_c('div',{staticClass:\"wrap\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.selected.y),expression:\"selected.y\"}],staticClass:\"input-range\",attrs:{\"disabled\":!_vm.present,\"type\":\"range\",\"max\":\"20\",\"min\":\"-20\"},domProps:{\"value\":(_vm.selected.y)},on:{\"__r\":function($event){_vm.$set(_vm.selected, \"y\", $event.target.value)}}})])]),_vm._v(\" \"),_c('div',{staticClass:\"preview-window\"},[_c('div',{staticClass:\"preview-block\",style:(_vm.style)})]),_vm._v(\" \"),_c('div',{staticClass:\"x-shift-control\",attrs:{\"disabled\":!_vm.present}},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.selected.x),expression:\"selected.x\"}],staticClass:\"input-number\",attrs:{\"disabled\":!_vm.present,\"type\":\"number\"},domProps:{\"value\":(_vm.selected.x)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.selected, \"x\", $event.target.value)}}}),_vm._v(\" \"),_c('div',{staticClass:\"wrap\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.selected.x),expression:\"selected.x\"}],staticClass:\"input-range\",attrs:{\"disabled\":!_vm.present,\"type\":\"range\",\"max\":\"20\",\"min\":\"-20\"},domProps:{\"value\":(_vm.selected.x)},on:{\"__r\":function($event){_vm.$set(_vm.selected, \"x\", $event.target.value)}}})])])]),_vm._v(\" \"),_c('div',{staticClass:\"shadow-tweak\"},[_c('div',{staticClass:\"id-control style-control\",attrs:{\"disabled\":_vm.usingFallback}},[_c('label',{staticClass:\"select\",attrs:{\"for\":\"shadow-switcher\",\"disabled\":!_vm.ready || _vm.usingFallback}},[_c('select',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.selectedId),expression:\"selectedId\"}],staticClass:\"shadow-switcher\",attrs:{\"id\":\"shadow-switcher\",\"disabled\":!_vm.ready || _vm.usingFallback},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.selectedId=$event.target.multiple ? $$selectedVal : $$selectedVal[0]}}},_vm._l((_vm.cValue),function(shadow,index){return _c('option',{key:index,domProps:{\"value\":index}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.style.shadows.shadow_id', { value: index }))+\"\\n \")])}),0),_vm._v(\" \"),_c('i',{staticClass:\"icon-down-open\"})]),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-default\",attrs:{\"disabled\":!_vm.ready || !_vm.present},on:{\"click\":_vm.del}},[_c('i',{staticClass:\"icon-cancel\"})]),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-default\",attrs:{\"disabled\":!_vm.moveUpValid},on:{\"click\":_vm.moveUp}},[_c('i',{staticClass:\"icon-up-open\"})]),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-default\",attrs:{\"disabled\":!_vm.moveDnValid},on:{\"click\":_vm.moveDn}},[_c('i',{staticClass:\"icon-down-open\"})]),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-default\",attrs:{\"disabled\":_vm.usingFallback},on:{\"click\":_vm.add}},[_c('i',{staticClass:\"icon-plus\"})])]),_vm._v(\" \"),_c('div',{staticClass:\"inset-control style-control\",attrs:{\"disabled\":!_vm.present}},[_c('label',{staticClass:\"label\",attrs:{\"for\":\"inset\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.style.shadows.inset'))+\"\\n \")]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.selected.inset),expression:\"selected.inset\"}],staticClass:\"input-inset\",attrs:{\"id\":\"inset\",\"disabled\":!_vm.present,\"name\":\"inset\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.selected.inset)?_vm._i(_vm.selected.inset,null)>-1:(_vm.selected.inset)},on:{\"change\":function($event){var $$a=_vm.selected.inset,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.$set(_vm.selected, \"inset\", $$a.concat([$$v])))}else{$$i>-1&&(_vm.$set(_vm.selected, \"inset\", $$a.slice(0,$$i).concat($$a.slice($$i+1))))}}else{_vm.$set(_vm.selected, \"inset\", $$c)}}}}),_vm._v(\" \"),_c('label',{staticClass:\"checkbox-label\",attrs:{\"for\":\"inset\"}})]),_vm._v(\" \"),_c('div',{staticClass:\"blur-control style-control\",attrs:{\"disabled\":!_vm.present}},[_c('label',{staticClass:\"label\",attrs:{\"for\":\"spread\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.style.shadows.blur'))+\"\\n \")]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.selected.blur),expression:\"selected.blur\"}],staticClass:\"input-range\",attrs:{\"id\":\"blur\",\"disabled\":!_vm.present,\"name\":\"blur\",\"type\":\"range\",\"max\":\"20\",\"min\":\"0\"},domProps:{\"value\":(_vm.selected.blur)},on:{\"__r\":function($event){_vm.$set(_vm.selected, \"blur\", $event.target.value)}}}),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.selected.blur),expression:\"selected.blur\"}],staticClass:\"input-number\",attrs:{\"disabled\":!_vm.present,\"type\":\"number\",\"min\":\"0\"},domProps:{\"value\":(_vm.selected.blur)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.selected, \"blur\", $event.target.value)}}})]),_vm._v(\" \"),_c('div',{staticClass:\"spread-control style-control\",attrs:{\"disabled\":!_vm.present}},[_c('label',{staticClass:\"label\",attrs:{\"for\":\"spread\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.style.shadows.spread'))+\"\\n \")]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.selected.spread),expression:\"selected.spread\"}],staticClass:\"input-range\",attrs:{\"id\":\"spread\",\"disabled\":!_vm.present,\"name\":\"spread\",\"type\":\"range\",\"max\":\"20\",\"min\":\"-20\"},domProps:{\"value\":(_vm.selected.spread)},on:{\"__r\":function($event){_vm.$set(_vm.selected, \"spread\", $event.target.value)}}}),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.selected.spread),expression:\"selected.spread\"}],staticClass:\"input-number\",attrs:{\"disabled\":!_vm.present,\"type\":\"number\"},domProps:{\"value\":(_vm.selected.spread)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.selected, \"spread\", $event.target.value)}}})]),_vm._v(\" \"),_c('ColorInput',{attrs:{\"disabled\":!_vm.present,\"label\":_vm.$t('settings.style.common.color'),\"name\":\"shadow\"},model:{value:(_vm.selected.color),callback:function ($$v) {_vm.$set(_vm.selected, \"color\", $$v)},expression:\"selected.color\"}}),_vm._v(\" \"),_c('OpacityInput',{attrs:{\"disabled\":!_vm.present},model:{value:(_vm.selected.alpha),callback:function ($$v) {_vm.$set(_vm.selected, \"alpha\", $$v)},expression:\"selected.alpha\"}}),_vm._v(\" \"),_c('p',[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.style.shadows.hint'))+\"\\n \")])],1)])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"font-control style-control\",class:{ custom: _vm.isCustom }},[_c('label',{staticClass:\"label\",attrs:{\"for\":_vm.preset === 'custom' ? _vm.name : _vm.name + '-font-switcher'}},[_vm._v(\"\\n \"+_vm._s(_vm.label)+\"\\n \")]),_vm._v(\" \"),(typeof _vm.fallback !== 'undefined')?_c('input',{staticClass:\"opt exlcude-disabled\",attrs:{\"id\":_vm.name + '-o',\"type\":\"checkbox\"},domProps:{\"checked\":_vm.present},on:{\"input\":function($event){_vm.$emit('input', typeof _vm.value === 'undefined' ? _vm.fallback : undefined)}}}):_vm._e(),_vm._v(\" \"),(typeof _vm.fallback !== 'undefined')?_c('label',{staticClass:\"opt-l\",attrs:{\"for\":_vm.name + '-o'}}):_vm._e(),_vm._v(\" \"),_c('label',{staticClass:\"select\",attrs:{\"for\":_vm.name + '-font-switcher',\"disabled\":!_vm.present}},[_c('select',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.preset),expression:\"preset\"}],staticClass:\"font-switcher\",attrs:{\"id\":_vm.name + '-font-switcher',\"disabled\":!_vm.present},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.preset=$event.target.multiple ? $$selectedVal : $$selectedVal[0]}}},_vm._l((_vm.availableOptions),function(option){return _c('option',{key:option,domProps:{\"value\":option}},[_vm._v(\"\\n \"+_vm._s(option === 'custom' ? _vm.$t('settings.style.fonts.custom') : option)+\"\\n \")])}),0),_vm._v(\" \"),_c('i',{staticClass:\"icon-down-open\"})]),_vm._v(\" \"),(_vm.isCustom)?_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.family),expression:\"family\"}],staticClass:\"custom-font\",attrs:{\"id\":_vm.name,\"type\":\"text\"},domProps:{\"value\":(_vm.family)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.family=$event.target.value}}}):_vm._e()])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return (_vm.contrast)?_c('span',{staticClass:\"contrast-ratio\"},[_c('span',{staticClass:\"rating\",attrs:{\"title\":_vm.hint}},[(_vm.contrast.aaa)?_c('span',[_c('i',{staticClass:\"icon-thumbs-up-alt\"})]):_vm._e(),_vm._v(\" \"),(!_vm.contrast.aaa && _vm.contrast.aa)?_c('span',[_c('i',{staticClass:\"icon-adjust\"})]):_vm._e(),_vm._v(\" \"),(!_vm.contrast.aaa && !_vm.contrast.aa)?_c('span',[_c('i',{staticClass:\"icon-attention\"})]):_vm._e()]),_vm._v(\" \"),(_vm.contrast && _vm.large)?_c('span',{staticClass:\"rating\",attrs:{\"title\":_vm.hint_18pt}},[(_vm.contrast.laaa)?_c('span',[_c('i',{staticClass:\"icon-thumbs-up-alt\"})]):_vm._e(),_vm._v(\" \"),(!_vm.contrast.laaa && _vm.contrast.laa)?_c('span',[_c('i',{staticClass:\"icon-adjust\"})]):_vm._e(),_vm._v(\" \"),(!_vm.contrast.laaa && !_vm.contrast.laa)?_c('span',[_c('i',{staticClass:\"icon-attention\"})]):_vm._e()]):_vm._e()]):_vm._e()}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"import-export-container\"},[_vm._t(\"before\"),_vm._v(\" \"),_c('button',{staticClass:\"btn\",on:{\"click\":_vm.exportData}},[_vm._v(\"\\n \"+_vm._s(_vm.exportLabel)+\"\\n \")]),_vm._v(\" \"),_c('button',{staticClass:\"btn\",on:{\"click\":_vm.importData}},[_vm._v(\"\\n \"+_vm._s(_vm.importLabel)+\"\\n \")]),_vm._v(\" \"),_vm._t(\"afterButtons\"),_vm._v(\" \"),(_vm.importFailed)?_c('p',{staticClass:\"alert error\"},[_vm._v(\"\\n \"+_vm._s(_vm.importFailedText)+\"\\n \")]):_vm._e(),_vm._v(\" \"),_vm._t(\"afterError\")],2)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"style-switcher\"},[_c('div',{staticClass:\"presets-container\"},[_c('div',{staticClass:\"save-load\"},[_c('export-import',{attrs:{\"export-object\":_vm.exportedTheme,\"export-label\":_vm.$t(\"settings.export_theme\"),\"import-label\":_vm.$t(\"settings.import_theme\"),\"import-failed-text\":_vm.$t(\"settings.invalid_theme_imported\"),\"on-import\":_vm.onImport,\"validator\":_vm.importValidator}},[_c('template',{slot:\"before\"},[_c('div',{staticClass:\"presets\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.presets'))+\"\\n \"),_c('label',{staticClass:\"select\",attrs:{\"for\":\"preset-switcher\"}},[_c('select',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.selected),expression:\"selected\"}],staticClass:\"preset-switcher\",attrs:{\"id\":\"preset-switcher\"},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.selected=$event.target.multiple ? $$selectedVal : $$selectedVal[0]}}},_vm._l((_vm.availableStyles),function(style){return _c('option',{key:style.name,style:({\n backgroundColor: style[1] || style.theme.colors.bg,\n color: style[3] || style.theme.colors.text\n }),domProps:{\"value\":style}},[_vm._v(\"\\n \"+_vm._s(style[0] || style.name)+\"\\n \")])}),0),_vm._v(\" \"),_c('i',{staticClass:\"icon-down-open\"})])])])],2)],1),_vm._v(\" \"),_c('div',{staticClass:\"save-load-options\"},[_c('span',{staticClass:\"keep-option\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.keepColor),expression:\"keepColor\"}],attrs:{\"id\":\"keep-color\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.keepColor)?_vm._i(_vm.keepColor,null)>-1:(_vm.keepColor)},on:{\"change\":function($event){var $$a=_vm.keepColor,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.keepColor=$$a.concat([$$v]))}else{$$i>-1&&(_vm.keepColor=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.keepColor=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"keep-color\"}},[_vm._v(_vm._s(_vm.$t('settings.style.switcher.keep_color')))])]),_vm._v(\" \"),_c('span',{staticClass:\"keep-option\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.keepShadows),expression:\"keepShadows\"}],attrs:{\"id\":\"keep-shadows\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.keepShadows)?_vm._i(_vm.keepShadows,null)>-1:(_vm.keepShadows)},on:{\"change\":function($event){var $$a=_vm.keepShadows,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.keepShadows=$$a.concat([$$v]))}else{$$i>-1&&(_vm.keepShadows=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.keepShadows=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"keep-shadows\"}},[_vm._v(_vm._s(_vm.$t('settings.style.switcher.keep_shadows')))])]),_vm._v(\" \"),_c('span',{staticClass:\"keep-option\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.keepOpacity),expression:\"keepOpacity\"}],attrs:{\"id\":\"keep-opacity\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.keepOpacity)?_vm._i(_vm.keepOpacity,null)>-1:(_vm.keepOpacity)},on:{\"change\":function($event){var $$a=_vm.keepOpacity,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.keepOpacity=$$a.concat([$$v]))}else{$$i>-1&&(_vm.keepOpacity=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.keepOpacity=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"keep-opacity\"}},[_vm._v(_vm._s(_vm.$t('settings.style.switcher.keep_opacity')))])]),_vm._v(\" \"),_c('span',{staticClass:\"keep-option\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.keepRoundness),expression:\"keepRoundness\"}],attrs:{\"id\":\"keep-roundness\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.keepRoundness)?_vm._i(_vm.keepRoundness,null)>-1:(_vm.keepRoundness)},on:{\"change\":function($event){var $$a=_vm.keepRoundness,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.keepRoundness=$$a.concat([$$v]))}else{$$i>-1&&(_vm.keepRoundness=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.keepRoundness=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"keep-roundness\"}},[_vm._v(_vm._s(_vm.$t('settings.style.switcher.keep_roundness')))])]),_vm._v(\" \"),_c('span',{staticClass:\"keep-option\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.keepFonts),expression:\"keepFonts\"}],attrs:{\"id\":\"keep-fonts\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.keepFonts)?_vm._i(_vm.keepFonts,null)>-1:(_vm.keepFonts)},on:{\"change\":function($event){var $$a=_vm.keepFonts,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.keepFonts=$$a.concat([$$v]))}else{$$i>-1&&(_vm.keepFonts=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.keepFonts=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"keep-fonts\"}},[_vm._v(_vm._s(_vm.$t('settings.style.switcher.keep_fonts')))])]),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.style.switcher.save_load_hint')))])])]),_vm._v(\" \"),_c('div',{staticClass:\"preview-container\"},[_c('preview',{style:(_vm.previewRules)})],1),_vm._v(\" \"),_c('keep-alive',[_c('tab-switcher',{key:\"style-tweak\"},[_c('div',{staticClass:\"color-container\",attrs:{\"label\":_vm.$t('settings.style.common_colors._tab_label')}},[_c('div',{staticClass:\"tab-header\"},[_c('p',[_vm._v(_vm._s(_vm.$t('settings.theme_help')))]),_vm._v(\" \"),_c('button',{staticClass:\"btn\",on:{\"click\":_vm.clearOpacity}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.style.switcher.clear_opacity'))+\"\\n \")]),_vm._v(\" \"),_c('button',{staticClass:\"btn\",on:{\"click\":_vm.clearV1}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.style.switcher.clear_all'))+\"\\n \")])]),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.theme_help_v2_1')))]),_vm._v(\" \"),_c('h4',[_vm._v(_vm._s(_vm.$t('settings.style.common_colors.main')))]),_vm._v(\" \"),_c('div',{staticClass:\"color-item\"},[_c('ColorInput',{attrs:{\"name\":\"bgColor\",\"label\":_vm.$t('settings.background')},model:{value:(_vm.bgColorLocal),callback:function ($$v) {_vm.bgColorLocal=$$v},expression:\"bgColorLocal\"}}),_vm._v(\" \"),_c('OpacityInput',{attrs:{\"name\":\"bgOpacity\",\"fallback\":_vm.previewTheme.opacity.bg || 1},model:{value:(_vm.bgOpacityLocal),callback:function ($$v) {_vm.bgOpacityLocal=$$v},expression:\"bgOpacityLocal\"}}),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"textColor\",\"label\":_vm.$t('settings.text')},model:{value:(_vm.textColorLocal),callback:function ($$v) {_vm.textColorLocal=$$v},expression:\"textColorLocal\"}}),_vm._v(\" \"),_c('ContrastRatio',{attrs:{\"contrast\":_vm.previewContrast.bgText}}),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"linkColor\",\"label\":_vm.$t('settings.links')},model:{value:(_vm.linkColorLocal),callback:function ($$v) {_vm.linkColorLocal=$$v},expression:\"linkColorLocal\"}}),_vm._v(\" \"),_c('ContrastRatio',{attrs:{\"contrast\":_vm.previewContrast.bgLink}})],1),_vm._v(\" \"),_c('div',{staticClass:\"color-item\"},[_c('ColorInput',{attrs:{\"name\":\"fgColor\",\"label\":_vm.$t('settings.foreground')},model:{value:(_vm.fgColorLocal),callback:function ($$v) {_vm.fgColorLocal=$$v},expression:\"fgColorLocal\"}}),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"fgTextColor\",\"label\":_vm.$t('settings.text'),\"fallback\":_vm.previewTheme.colors.fgText},model:{value:(_vm.fgTextColorLocal),callback:function ($$v) {_vm.fgTextColorLocal=$$v},expression:\"fgTextColorLocal\"}}),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"fgLinkColor\",\"label\":_vm.$t('settings.links'),\"fallback\":_vm.previewTheme.colors.fgLink},model:{value:(_vm.fgLinkColorLocal),callback:function ($$v) {_vm.fgLinkColorLocal=$$v},expression:\"fgLinkColorLocal\"}}),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.style.common_colors.foreground_hint')))])],1),_vm._v(\" \"),_c('h4',[_vm._v(_vm._s(_vm.$t('settings.style.common_colors.rgbo')))]),_vm._v(\" \"),_c('div',{staticClass:\"color-item\"},[_c('ColorInput',{attrs:{\"name\":\"cRedColor\",\"label\":_vm.$t('settings.cRed')},model:{value:(_vm.cRedColorLocal),callback:function ($$v) {_vm.cRedColorLocal=$$v},expression:\"cRedColorLocal\"}}),_vm._v(\" \"),_c('ContrastRatio',{attrs:{\"contrast\":_vm.previewContrast.bgRed}}),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"cBlueColor\",\"label\":_vm.$t('settings.cBlue')},model:{value:(_vm.cBlueColorLocal),callback:function ($$v) {_vm.cBlueColorLocal=$$v},expression:\"cBlueColorLocal\"}}),_vm._v(\" \"),_c('ContrastRatio',{attrs:{\"contrast\":_vm.previewContrast.bgBlue}})],1),_vm._v(\" \"),_c('div',{staticClass:\"color-item\"},[_c('ColorInput',{attrs:{\"name\":\"cGreenColor\",\"label\":_vm.$t('settings.cGreen')},model:{value:(_vm.cGreenColorLocal),callback:function ($$v) {_vm.cGreenColorLocal=$$v},expression:\"cGreenColorLocal\"}}),_vm._v(\" \"),_c('ContrastRatio',{attrs:{\"contrast\":_vm.previewContrast.bgGreen}}),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"cOrangeColor\",\"label\":_vm.$t('settings.cOrange')},model:{value:(_vm.cOrangeColorLocal),callback:function ($$v) {_vm.cOrangeColorLocal=$$v},expression:\"cOrangeColorLocal\"}}),_vm._v(\" \"),_c('ContrastRatio',{attrs:{\"contrast\":_vm.previewContrast.bgOrange}})],1),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.theme_help_v2_2')))])]),_vm._v(\" \"),_c('div',{staticClass:\"color-container\",attrs:{\"label\":_vm.$t('settings.style.advanced_colors._tab_label')}},[_c('div',{staticClass:\"tab-header\"},[_c('p',[_vm._v(_vm._s(_vm.$t('settings.theme_help')))]),_vm._v(\" \"),_c('button',{staticClass:\"btn\",on:{\"click\":_vm.clearOpacity}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.style.switcher.clear_opacity'))+\"\\n \")]),_vm._v(\" \"),_c('button',{staticClass:\"btn\",on:{\"click\":_vm.clearV1}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.style.switcher.clear_all'))+\"\\n \")])]),_vm._v(\" \"),_c('div',{staticClass:\"color-item\"},[_c('h4',[_vm._v(_vm._s(_vm.$t('settings.style.advanced_colors.alert')))]),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"alertError\",\"label\":_vm.$t('settings.style.advanced_colors.alert_error'),\"fallback\":_vm.previewTheme.colors.alertError},model:{value:(_vm.alertErrorColorLocal),callback:function ($$v) {_vm.alertErrorColorLocal=$$v},expression:\"alertErrorColorLocal\"}}),_vm._v(\" \"),_c('ContrastRatio',{attrs:{\"contrast\":_vm.previewContrast.alertError}})],1),_vm._v(\" \"),_c('div',{staticClass:\"color-item\"},[_c('h4',[_vm._v(_vm._s(_vm.$t('settings.style.advanced_colors.badge')))]),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"badgeNotification\",\"label\":_vm.$t('settings.style.advanced_colors.badge_notification'),\"fallback\":_vm.previewTheme.colors.badgeNotification},model:{value:(_vm.badgeNotificationColorLocal),callback:function ($$v) {_vm.badgeNotificationColorLocal=$$v},expression:\"badgeNotificationColorLocal\"}})],1),_vm._v(\" \"),_c('div',{staticClass:\"color-item\"},[_c('h4',[_vm._v(_vm._s(_vm.$t('settings.style.advanced_colors.panel_header')))]),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"panelColor\",\"fallback\":_vm.fgColorLocal,\"label\":_vm.$t('settings.background')},model:{value:(_vm.panelColorLocal),callback:function ($$v) {_vm.panelColorLocal=$$v},expression:\"panelColorLocal\"}}),_vm._v(\" \"),_c('OpacityInput',{attrs:{\"name\":\"panelOpacity\",\"fallback\":_vm.previewTheme.opacity.panel || 1},model:{value:(_vm.panelOpacityLocal),callback:function ($$v) {_vm.panelOpacityLocal=$$v},expression:\"panelOpacityLocal\"}}),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"panelTextColor\",\"fallback\":_vm.previewTheme.colors.panelText,\"label\":_vm.$t('settings.text')},model:{value:(_vm.panelTextColorLocal),callback:function ($$v) {_vm.panelTextColorLocal=$$v},expression:\"panelTextColorLocal\"}}),_vm._v(\" \"),_c('ContrastRatio',{attrs:{\"contrast\":_vm.previewContrast.panelText,\"large\":\"1\"}}),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"panelLinkColor\",\"fallback\":_vm.previewTheme.colors.panelLink,\"label\":_vm.$t('settings.links')},model:{value:(_vm.panelLinkColorLocal),callback:function ($$v) {_vm.panelLinkColorLocal=$$v},expression:\"panelLinkColorLocal\"}}),_vm._v(\" \"),_c('ContrastRatio',{attrs:{\"contrast\":_vm.previewContrast.panelLink,\"large\":\"1\"}})],1),_vm._v(\" \"),_c('div',{staticClass:\"color-item\"},[_c('h4',[_vm._v(_vm._s(_vm.$t('settings.style.advanced_colors.top_bar')))]),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"topBarColor\",\"fallback\":_vm.fgColorLocal,\"label\":_vm.$t('settings.background')},model:{value:(_vm.topBarColorLocal),callback:function ($$v) {_vm.topBarColorLocal=$$v},expression:\"topBarColorLocal\"}}),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"topBarTextColor\",\"fallback\":_vm.previewTheme.colors.topBarText,\"label\":_vm.$t('settings.text')},model:{value:(_vm.topBarTextColorLocal),callback:function ($$v) {_vm.topBarTextColorLocal=$$v},expression:\"topBarTextColorLocal\"}}),_vm._v(\" \"),_c('ContrastRatio',{attrs:{\"contrast\":_vm.previewContrast.topBarText}}),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"topBarLinkColor\",\"fallback\":_vm.previewTheme.colors.topBarLink,\"label\":_vm.$t('settings.links')},model:{value:(_vm.topBarLinkColorLocal),callback:function ($$v) {_vm.topBarLinkColorLocal=$$v},expression:\"topBarLinkColorLocal\"}}),_vm._v(\" \"),_c('ContrastRatio',{attrs:{\"contrast\":_vm.previewContrast.topBarLink}})],1),_vm._v(\" \"),_c('div',{staticClass:\"color-item\"},[_c('h4',[_vm._v(_vm._s(_vm.$t('settings.style.advanced_colors.inputs')))]),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"inputColor\",\"fallback\":_vm.fgColorLocal,\"label\":_vm.$t('settings.background')},model:{value:(_vm.inputColorLocal),callback:function ($$v) {_vm.inputColorLocal=$$v},expression:\"inputColorLocal\"}}),_vm._v(\" \"),_c('OpacityInput',{attrs:{\"name\":\"inputOpacity\",\"fallback\":_vm.previewTheme.opacity.input || 1},model:{value:(_vm.inputOpacityLocal),callback:function ($$v) {_vm.inputOpacityLocal=$$v},expression:\"inputOpacityLocal\"}}),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"inputTextColor\",\"fallback\":_vm.previewTheme.colors.inputText,\"label\":_vm.$t('settings.text')},model:{value:(_vm.inputTextColorLocal),callback:function ($$v) {_vm.inputTextColorLocal=$$v},expression:\"inputTextColorLocal\"}}),_vm._v(\" \"),_c('ContrastRatio',{attrs:{\"contrast\":_vm.previewContrast.inputText}})],1),_vm._v(\" \"),_c('div',{staticClass:\"color-item\"},[_c('h4',[_vm._v(_vm._s(_vm.$t('settings.style.advanced_colors.buttons')))]),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"btnColor\",\"fallback\":_vm.fgColorLocal,\"label\":_vm.$t('settings.background')},model:{value:(_vm.btnColorLocal),callback:function ($$v) {_vm.btnColorLocal=$$v},expression:\"btnColorLocal\"}}),_vm._v(\" \"),_c('OpacityInput',{attrs:{\"name\":\"btnOpacity\",\"fallback\":_vm.previewTheme.opacity.btn || 1},model:{value:(_vm.btnOpacityLocal),callback:function ($$v) {_vm.btnOpacityLocal=$$v},expression:\"btnOpacityLocal\"}}),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"btnTextColor\",\"fallback\":_vm.previewTheme.colors.btnText,\"label\":_vm.$t('settings.text')},model:{value:(_vm.btnTextColorLocal),callback:function ($$v) {_vm.btnTextColorLocal=$$v},expression:\"btnTextColorLocal\"}}),_vm._v(\" \"),_c('ContrastRatio',{attrs:{\"contrast\":_vm.previewContrast.btnText}})],1),_vm._v(\" \"),_c('div',{staticClass:\"color-item\"},[_c('h4',[_vm._v(_vm._s(_vm.$t('settings.style.advanced_colors.borders')))]),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"borderColor\",\"fallback\":_vm.previewTheme.colors.border,\"label\":_vm.$t('settings.style.common.color')},model:{value:(_vm.borderColorLocal),callback:function ($$v) {_vm.borderColorLocal=$$v},expression:\"borderColorLocal\"}}),_vm._v(\" \"),_c('OpacityInput',{attrs:{\"name\":\"borderOpacity\",\"fallback\":_vm.previewTheme.opacity.border || 1},model:{value:(_vm.borderOpacityLocal),callback:function ($$v) {_vm.borderOpacityLocal=$$v},expression:\"borderOpacityLocal\"}})],1),_vm._v(\" \"),_c('div',{staticClass:\"color-item\"},[_c('h4',[_vm._v(_vm._s(_vm.$t('settings.style.advanced_colors.faint_text')))]),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"faintColor\",\"fallback\":_vm.previewTheme.colors.faint || 1,\"label\":_vm.$t('settings.text')},model:{value:(_vm.faintColorLocal),callback:function ($$v) {_vm.faintColorLocal=$$v},expression:\"faintColorLocal\"}}),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"faintLinkColor\",\"fallback\":_vm.previewTheme.colors.faintLink,\"label\":_vm.$t('settings.links')},model:{value:(_vm.faintLinkColorLocal),callback:function ($$v) {_vm.faintLinkColorLocal=$$v},expression:\"faintLinkColorLocal\"}}),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"panelFaintColor\",\"fallback\":_vm.previewTheme.colors.panelFaint,\"label\":_vm.$t('settings.style.advanced_colors.panel_header')},model:{value:(_vm.panelFaintColorLocal),callback:function ($$v) {_vm.panelFaintColorLocal=$$v},expression:\"panelFaintColorLocal\"}}),_vm._v(\" \"),_c('OpacityInput',{attrs:{\"name\":\"faintOpacity\",\"fallback\":_vm.previewTheme.opacity.faint || 0.5},model:{value:(_vm.faintOpacityLocal),callback:function ($$v) {_vm.faintOpacityLocal=$$v},expression:\"faintOpacityLocal\"}})],1)]),_vm._v(\" \"),_c('div',{staticClass:\"radius-container\",attrs:{\"label\":_vm.$t('settings.style.radii._tab_label')}},[_c('div',{staticClass:\"tab-header\"},[_c('p',[_vm._v(_vm._s(_vm.$t('settings.radii_help')))]),_vm._v(\" \"),_c('button',{staticClass:\"btn\",on:{\"click\":_vm.clearRoundness}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.style.switcher.clear_all'))+\"\\n \")])]),_vm._v(\" \"),_c('RangeInput',{attrs:{\"name\":\"btnRadius\",\"label\":_vm.$t('settings.btnRadius'),\"fallback\":_vm.previewTheme.radii.btn,\"max\":\"16\",\"hard-min\":\"0\"},model:{value:(_vm.btnRadiusLocal),callback:function ($$v) {_vm.btnRadiusLocal=$$v},expression:\"btnRadiusLocal\"}}),_vm._v(\" \"),_c('RangeInput',{attrs:{\"name\":\"inputRadius\",\"label\":_vm.$t('settings.inputRadius'),\"fallback\":_vm.previewTheme.radii.input,\"max\":\"9\",\"hard-min\":\"0\"},model:{value:(_vm.inputRadiusLocal),callback:function ($$v) {_vm.inputRadiusLocal=$$v},expression:\"inputRadiusLocal\"}}),_vm._v(\" \"),_c('RangeInput',{attrs:{\"name\":\"checkboxRadius\",\"label\":_vm.$t('settings.checkboxRadius'),\"fallback\":_vm.previewTheme.radii.checkbox,\"max\":\"16\",\"hard-min\":\"0\"},model:{value:(_vm.checkboxRadiusLocal),callback:function ($$v) {_vm.checkboxRadiusLocal=$$v},expression:\"checkboxRadiusLocal\"}}),_vm._v(\" \"),_c('RangeInput',{attrs:{\"name\":\"panelRadius\",\"label\":_vm.$t('settings.panelRadius'),\"fallback\":_vm.previewTheme.radii.panel,\"max\":\"50\",\"hard-min\":\"0\"},model:{value:(_vm.panelRadiusLocal),callback:function ($$v) {_vm.panelRadiusLocal=$$v},expression:\"panelRadiusLocal\"}}),_vm._v(\" \"),_c('RangeInput',{attrs:{\"name\":\"avatarRadius\",\"label\":_vm.$t('settings.avatarRadius'),\"fallback\":_vm.previewTheme.radii.avatar,\"max\":\"28\",\"hard-min\":\"0\"},model:{value:(_vm.avatarRadiusLocal),callback:function ($$v) {_vm.avatarRadiusLocal=$$v},expression:\"avatarRadiusLocal\"}}),_vm._v(\" \"),_c('RangeInput',{attrs:{\"name\":\"avatarAltRadius\",\"label\":_vm.$t('settings.avatarAltRadius'),\"fallback\":_vm.previewTheme.radii.avatarAlt,\"max\":\"28\",\"hard-min\":\"0\"},model:{value:(_vm.avatarAltRadiusLocal),callback:function ($$v) {_vm.avatarAltRadiusLocal=$$v},expression:\"avatarAltRadiusLocal\"}}),_vm._v(\" \"),_c('RangeInput',{attrs:{\"name\":\"attachmentRadius\",\"label\":_vm.$t('settings.attachmentRadius'),\"fallback\":_vm.previewTheme.radii.attachment,\"max\":\"50\",\"hard-min\":\"0\"},model:{value:(_vm.attachmentRadiusLocal),callback:function ($$v) {_vm.attachmentRadiusLocal=$$v},expression:\"attachmentRadiusLocal\"}}),_vm._v(\" \"),_c('RangeInput',{attrs:{\"name\":\"tooltipRadius\",\"label\":_vm.$t('settings.tooltipRadius'),\"fallback\":_vm.previewTheme.radii.tooltip,\"max\":\"50\",\"hard-min\":\"0\"},model:{value:(_vm.tooltipRadiusLocal),callback:function ($$v) {_vm.tooltipRadiusLocal=$$v},expression:\"tooltipRadiusLocal\"}})],1),_vm._v(\" \"),_c('div',{staticClass:\"shadow-container\",attrs:{\"label\":_vm.$t('settings.style.shadows._tab_label')}},[_c('div',{staticClass:\"tab-header shadow-selector\"},[_c('div',{staticClass:\"select-container\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.style.shadows.component'))+\"\\n \"),_c('label',{staticClass:\"select\",attrs:{\"for\":\"shadow-switcher\"}},[_c('select',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.shadowSelected),expression:\"shadowSelected\"}],staticClass:\"shadow-switcher\",attrs:{\"id\":\"shadow-switcher\"},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.shadowSelected=$event.target.multiple ? $$selectedVal : $$selectedVal[0]}}},_vm._l((_vm.shadowsAvailable),function(shadow){return _c('option',{key:shadow,domProps:{\"value\":shadow}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.style.shadows.components.' + shadow))+\"\\n \")])}),0),_vm._v(\" \"),_c('i',{staticClass:\"icon-down-open\"})])]),_vm._v(\" \"),_c('div',{staticClass:\"override\"},[_c('label',{staticClass:\"label\",attrs:{\"for\":\"override\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.style.shadows.override'))+\"\\n \")]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.currentShadowOverriden),expression:\"currentShadowOverriden\"}],staticClass:\"input-override\",attrs:{\"id\":\"override\",\"name\":\"override\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.currentShadowOverriden)?_vm._i(_vm.currentShadowOverriden,null)>-1:(_vm.currentShadowOverriden)},on:{\"change\":function($event){var $$a=_vm.currentShadowOverriden,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.currentShadowOverriden=$$a.concat([$$v]))}else{$$i>-1&&(_vm.currentShadowOverriden=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.currentShadowOverriden=$$c}}}}),_vm._v(\" \"),_c('label',{staticClass:\"checkbox-label\",attrs:{\"for\":\"override\"}})]),_vm._v(\" \"),_c('button',{staticClass:\"btn\",on:{\"click\":_vm.clearShadows}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.style.switcher.clear_all'))+\"\\n \")])]),_vm._v(\" \"),_c('shadow-control',{attrs:{\"ready\":!!_vm.currentShadowFallback,\"fallback\":_vm.currentShadowFallback},model:{value:(_vm.currentShadow),callback:function ($$v) {_vm.currentShadow=$$v},expression:\"currentShadow\"}}),_vm._v(\" \"),(_vm.shadowSelected === 'avatar' || _vm.shadowSelected === 'avatarStatus')?_c('div',[_c('i18n',{attrs:{\"path\":\"settings.style.shadows.filter_hint.always_drop_shadow\",\"tag\":\"p\"}},[_c('code',[_vm._v(\"filter: drop-shadow()\")])]),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.style.shadows.filter_hint.avatar_inset')))]),_vm._v(\" \"),_c('i18n',{attrs:{\"path\":\"settings.style.shadows.filter_hint.drop_shadow_syntax\",\"tag\":\"p\"}},[_c('code',[_vm._v(\"drop-shadow\")]),_vm._v(\" \"),_c('code',[_vm._v(\"spread-radius\")]),_vm._v(\" \"),_c('code',[_vm._v(\"inset\")])]),_vm._v(\" \"),_c('i18n',{attrs:{\"path\":\"settings.style.shadows.filter_hint.inset_classic\",\"tag\":\"p\"}},[_c('code',[_vm._v(\"box-shadow\")])]),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.style.shadows.filter_hint.spread_zero')))])],1):_vm._e()],1),_vm._v(\" \"),_c('div',{staticClass:\"fonts-container\",attrs:{\"label\":_vm.$t('settings.style.fonts._tab_label')}},[_c('div',{staticClass:\"tab-header\"},[_c('p',[_vm._v(_vm._s(_vm.$t('settings.style.fonts.help')))]),_vm._v(\" \"),_c('button',{staticClass:\"btn\",on:{\"click\":_vm.clearFonts}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.style.switcher.clear_all'))+\"\\n \")])]),_vm._v(\" \"),_c('FontControl',{attrs:{\"name\":\"ui\",\"label\":_vm.$t('settings.style.fonts.components.interface'),\"fallback\":_vm.previewTheme.fonts.interface,\"no-inherit\":\"1\"},model:{value:(_vm.fontsLocal.interface),callback:function ($$v) {_vm.$set(_vm.fontsLocal, \"interface\", $$v)},expression:\"fontsLocal.interface\"}}),_vm._v(\" \"),_c('FontControl',{attrs:{\"name\":\"input\",\"label\":_vm.$t('settings.style.fonts.components.input'),\"fallback\":_vm.previewTheme.fonts.input},model:{value:(_vm.fontsLocal.input),callback:function ($$v) {_vm.$set(_vm.fontsLocal, \"input\", $$v)},expression:\"fontsLocal.input\"}}),_vm._v(\" \"),_c('FontControl',{attrs:{\"name\":\"post\",\"label\":_vm.$t('settings.style.fonts.components.post'),\"fallback\":_vm.previewTheme.fonts.post},model:{value:(_vm.fontsLocal.post),callback:function ($$v) {_vm.$set(_vm.fontsLocal, \"post\", $$v)},expression:\"fontsLocal.post\"}}),_vm._v(\" \"),_c('FontControl',{attrs:{\"name\":\"postCode\",\"label\":_vm.$t('settings.style.fonts.components.postCode'),\"fallback\":_vm.previewTheme.fonts.postCode},model:{value:(_vm.fontsLocal.postCode),callback:function ($$v) {_vm.$set(_vm.fontsLocal, \"postCode\", $$v)},expression:\"fontsLocal.postCode\"}})],1)])],1),_vm._v(\" \"),_c('div',{staticClass:\"apply-container\"},[_c('button',{staticClass:\"btn submit\",attrs:{\"disabled\":!_vm.themeValid},on:{\"click\":_vm.setCustomTheme}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.apply'))+\"\\n \")]),_vm._v(\" \"),_c('button',{staticClass:\"btn\",on:{\"click\":_vm.clearAll}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.style.switcher.reset'))+\"\\n \")])])],1)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('label',{attrs:{\"for\":\"interface-language-switcher\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.interfaceLanguage'))+\"\\n \")]),_vm._v(\" \"),_c('label',{staticClass:\"select\",attrs:{\"for\":\"interface-language-switcher\"}},[_c('select',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.language),expression:\"language\"}],attrs:{\"id\":\"interface-language-switcher\"},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.language=$event.target.multiple ? $$selectedVal : $$selectedVal[0]}}},_vm._l((_vm.languageCodes),function(langCode,i){return _c('option',{key:langCode,domProps:{\"value\":langCode}},[_vm._v(\"\\n \"+_vm._s(_vm.languageNames[i])+\"\\n \")])}),0),_vm._v(\" \"),_c('i',{staticClass:\"icon-down-open\"})])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"settings panel panel-default\"},[_c('div',{staticClass:\"panel-heading\"},[_c('div',{staticClass:\"title\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.settings'))+\"\\n \")]),_vm._v(\" \"),_c('transition',{attrs:{\"name\":\"fade\"}},[(_vm.currentSaveStateNotice)?[(_vm.currentSaveStateNotice.error)?_c('div',{staticClass:\"alert error\",on:{\"click\":function($event){$event.preventDefault();}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.saving_err'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(!_vm.currentSaveStateNotice.error)?_c('div',{staticClass:\"alert transparent\",on:{\"click\":function($event){$event.preventDefault();}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.saving_ok'))+\"\\n \")]):_vm._e()]:_vm._e()],2)],1),_vm._v(\" \"),_c('div',{staticClass:\"panel-body\"},[_c('keep-alive',[_c('tab-switcher',[_c('div',{attrs:{\"label\":_vm.$t('settings.general')}},[_c('div',{staticClass:\"setting-item\"},[_c('h2',[_vm._v(_vm._s(_vm.$t('settings.interface')))]),_vm._v(\" \"),_c('ul',{staticClass:\"setting-list\"},[_c('li',[_c('interface-language-switcher')],1),_vm._v(\" \"),(_vm.instanceSpecificPanelPresent)?_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.hideISPLocal),expression:\"hideISPLocal\"}],attrs:{\"id\":\"hideISP\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.hideISPLocal)?_vm._i(_vm.hideISPLocal,null)>-1:(_vm.hideISPLocal)},on:{\"change\":function($event){var $$a=_vm.hideISPLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.hideISPLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.hideISPLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.hideISPLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"hideISP\"}},[_vm._v(_vm._s(_vm.$t('settings.hide_isp')))])]):_vm._e()])]),_vm._v(\" \"),_c('div',{staticClass:\"setting-item\"},[_c('h2',[_vm._v(_vm._s(_vm.$t('nav.timeline')))]),_vm._v(\" \"),_c('ul',{staticClass:\"setting-list\"},[_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.hideMutedPostsLocal),expression:\"hideMutedPostsLocal\"}],attrs:{\"id\":\"hideMutedPosts\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.hideMutedPostsLocal)?_vm._i(_vm.hideMutedPostsLocal,null)>-1:(_vm.hideMutedPostsLocal)},on:{\"change\":function($event){var $$a=_vm.hideMutedPostsLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.hideMutedPostsLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.hideMutedPostsLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.hideMutedPostsLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"hideMutedPosts\"}},[_vm._v(_vm._s(_vm.$t('settings.hide_muted_posts'))+\" \"+_vm._s(_vm.$t('settings.instance_default', { value: _vm.hideMutedPostsDefault })))])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.collapseMessageWithSubjectLocal),expression:\"collapseMessageWithSubjectLocal\"}],attrs:{\"id\":\"collapseMessageWithSubject\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.collapseMessageWithSubjectLocal)?_vm._i(_vm.collapseMessageWithSubjectLocal,null)>-1:(_vm.collapseMessageWithSubjectLocal)},on:{\"change\":function($event){var $$a=_vm.collapseMessageWithSubjectLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.collapseMessageWithSubjectLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.collapseMessageWithSubjectLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.collapseMessageWithSubjectLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"collapseMessageWithSubject\"}},[_vm._v(_vm._s(_vm.$t('settings.collapse_subject'))+\" \"+_vm._s(_vm.$t('settings.instance_default', { value: _vm.collapseMessageWithSubjectDefault })))])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.streamingLocal),expression:\"streamingLocal\"}],attrs:{\"id\":\"streaming\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.streamingLocal)?_vm._i(_vm.streamingLocal,null)>-1:(_vm.streamingLocal)},on:{\"change\":function($event){var $$a=_vm.streamingLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.streamingLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.streamingLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.streamingLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"streaming\"}},[_vm._v(_vm._s(_vm.$t('settings.streaming')))]),_vm._v(\" \"),_c('ul',{staticClass:\"setting-list suboptions\",class:[{disabled: !_vm.streamingLocal}]},[_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.pauseOnUnfocusedLocal),expression:\"pauseOnUnfocusedLocal\"}],attrs:{\"id\":\"pauseOnUnfocused\",\"disabled\":!_vm.streamingLocal,\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.pauseOnUnfocusedLocal)?_vm._i(_vm.pauseOnUnfocusedLocal,null)>-1:(_vm.pauseOnUnfocusedLocal)},on:{\"change\":function($event){var $$a=_vm.pauseOnUnfocusedLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.pauseOnUnfocusedLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.pauseOnUnfocusedLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.pauseOnUnfocusedLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"pauseOnUnfocused\"}},[_vm._v(_vm._s(_vm.$t('settings.pause_on_unfocused')))])])])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.autoLoadLocal),expression:\"autoLoadLocal\"}],attrs:{\"id\":\"autoload\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.autoLoadLocal)?_vm._i(_vm.autoLoadLocal,null)>-1:(_vm.autoLoadLocal)},on:{\"change\":function($event){var $$a=_vm.autoLoadLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.autoLoadLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.autoLoadLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.autoLoadLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"autoload\"}},[_vm._v(_vm._s(_vm.$t('settings.autoload')))])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.hoverPreviewLocal),expression:\"hoverPreviewLocal\"}],attrs:{\"id\":\"hoverPreview\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.hoverPreviewLocal)?_vm._i(_vm.hoverPreviewLocal,null)>-1:(_vm.hoverPreviewLocal)},on:{\"change\":function($event){var $$a=_vm.hoverPreviewLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.hoverPreviewLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.hoverPreviewLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.hoverPreviewLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"hoverPreview\"}},[_vm._v(_vm._s(_vm.$t('settings.reply_link_preview')))])])])]),_vm._v(\" \"),_c('div',{staticClass:\"setting-item\"},[_c('h2',[_vm._v(_vm._s(_vm.$t('settings.composing')))]),_vm._v(\" \"),_c('ul',{staticClass:\"setting-list\"},[_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.scopeCopyLocal),expression:\"scopeCopyLocal\"}],attrs:{\"id\":\"scopeCopy\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.scopeCopyLocal)?_vm._i(_vm.scopeCopyLocal,null)>-1:(_vm.scopeCopyLocal)},on:{\"change\":function($event){var $$a=_vm.scopeCopyLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.scopeCopyLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.scopeCopyLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.scopeCopyLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"scopeCopy\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.scope_copy'))+\" \"+_vm._s(_vm.$t('settings.instance_default', { value: _vm.scopeCopyDefault }))+\"\\n \")])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.alwaysShowSubjectInputLocal),expression:\"alwaysShowSubjectInputLocal\"}],attrs:{\"id\":\"subjectHide\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.alwaysShowSubjectInputLocal)?_vm._i(_vm.alwaysShowSubjectInputLocal,null)>-1:(_vm.alwaysShowSubjectInputLocal)},on:{\"change\":function($event){var $$a=_vm.alwaysShowSubjectInputLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.alwaysShowSubjectInputLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.alwaysShowSubjectInputLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.alwaysShowSubjectInputLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"subjectHide\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.subject_input_always_show'))+\" \"+_vm._s(_vm.$t('settings.instance_default', { value: _vm.alwaysShowSubjectInputDefault }))+\"\\n \")])]),_vm._v(\" \"),_c('li',[_c('div',[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.subject_line_behavior'))+\"\\n \"),_c('label',{staticClass:\"select\",attrs:{\"for\":\"subjectLineBehavior\"}},[_c('select',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.subjectLineBehaviorLocal),expression:\"subjectLineBehaviorLocal\"}],attrs:{\"id\":\"subjectLineBehavior\"},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.subjectLineBehaviorLocal=$event.target.multiple ? $$selectedVal : $$selectedVal[0]}}},[_c('option',{attrs:{\"value\":\"email\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.subject_line_email'))+\"\\n \"+_vm._s(_vm.subjectLineBehaviorDefault == 'email' ? _vm.$t('settings.instance_default_simple') : '')+\"\\n \")]),_vm._v(\" \"),_c('option',{attrs:{\"value\":\"masto\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.subject_line_mastodon'))+\"\\n \"+_vm._s(_vm.subjectLineBehaviorDefault == 'mastodon' ? _vm.$t('settings.instance_default_simple') : '')+\"\\n \")]),_vm._v(\" \"),_c('option',{attrs:{\"value\":\"noop\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.subject_line_noop'))+\"\\n \"+_vm._s(_vm.subjectLineBehaviorDefault == 'noop' ? _vm.$t('settings.instance_default_simple') : '')+\"\\n \")])]),_vm._v(\" \"),_c('i',{staticClass:\"icon-down-open\"})])])]),_vm._v(\" \"),(_vm.postFormats.length > 0)?_c('li',[_c('div',[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.post_status_content_type'))+\"\\n \"),_c('label',{staticClass:\"select\",attrs:{\"for\":\"postContentType\"}},[_c('select',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.postContentTypeLocal),expression:\"postContentTypeLocal\"}],attrs:{\"id\":\"postContentType\"},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.postContentTypeLocal=$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 \"+_vm._s(_vm.postContentTypeDefault === postFormat ? _vm.$t('settings.instance_default_simple') : '')+\"\\n \")])}),0),_vm._v(\" \"),_c('i',{staticClass:\"icon-down-open\"})])])]):_vm._e(),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.minimalScopesModeLocal),expression:\"minimalScopesModeLocal\"}],attrs:{\"id\":\"minimalScopesMode\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.minimalScopesModeLocal)?_vm._i(_vm.minimalScopesModeLocal,null)>-1:(_vm.minimalScopesModeLocal)},on:{\"change\":function($event){var $$a=_vm.minimalScopesModeLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.minimalScopesModeLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.minimalScopesModeLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.minimalScopesModeLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"minimalScopesMode\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.minimal_scopes_mode'))+\" \"+_vm._s(_vm.$t('settings.instance_default', { value: _vm.minimalScopesModeDefault }))+\"\\n \")])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.autohideFloatingPostButtonLocal),expression:\"autohideFloatingPostButtonLocal\"}],attrs:{\"id\":\"autohideFloatingPostButton\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.autohideFloatingPostButtonLocal)?_vm._i(_vm.autohideFloatingPostButtonLocal,null)>-1:(_vm.autohideFloatingPostButtonLocal)},on:{\"change\":function($event){var $$a=_vm.autohideFloatingPostButtonLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.autohideFloatingPostButtonLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.autohideFloatingPostButtonLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.autohideFloatingPostButtonLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"autohideFloatingPostButton\"}},[_vm._v(_vm._s(_vm.$t('settings.autohide_floating_post_button')))])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.padEmojiLocal),expression:\"padEmojiLocal\"}],attrs:{\"id\":\"padEmoji\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.padEmojiLocal)?_vm._i(_vm.padEmojiLocal,null)>-1:(_vm.padEmojiLocal)},on:{\"change\":function($event){var $$a=_vm.padEmojiLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.padEmojiLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.padEmojiLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.padEmojiLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"padEmoji\"}},[_vm._v(_vm._s(_vm.$t('settings.pad_emoji')))])])])]),_vm._v(\" \"),_c('div',{staticClass:\"setting-item\"},[_c('h2',[_vm._v(_vm._s(_vm.$t('settings.attachments')))]),_vm._v(\" \"),_c('ul',{staticClass:\"setting-list\"},[_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.hideAttachmentsLocal),expression:\"hideAttachmentsLocal\"}],attrs:{\"id\":\"hideAttachments\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.hideAttachmentsLocal)?_vm._i(_vm.hideAttachmentsLocal,null)>-1:(_vm.hideAttachmentsLocal)},on:{\"change\":function($event){var $$a=_vm.hideAttachmentsLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.hideAttachmentsLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.hideAttachmentsLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.hideAttachmentsLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"hideAttachments\"}},[_vm._v(_vm._s(_vm.$t('settings.hide_attachments_in_tl')))])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.hideAttachmentsInConvLocal),expression:\"hideAttachmentsInConvLocal\"}],attrs:{\"id\":\"hideAttachmentsInConv\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.hideAttachmentsInConvLocal)?_vm._i(_vm.hideAttachmentsInConvLocal,null)>-1:(_vm.hideAttachmentsInConvLocal)},on:{\"change\":function($event){var $$a=_vm.hideAttachmentsInConvLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.hideAttachmentsInConvLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.hideAttachmentsInConvLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.hideAttachmentsInConvLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"hideAttachmentsInConv\"}},[_vm._v(_vm._s(_vm.$t('settings.hide_attachments_in_convo')))])]),_vm._v(\" \"),_c('li',[_c('label',{attrs:{\"for\":\"maxThumbnails\"}},[_vm._v(_vm._s(_vm.$t('settings.max_thumbnails')))]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model.number\",value:(_vm.maxThumbnails),expression:\"maxThumbnails\",modifiers:{\"number\":true}}],staticClass:\"number-input\",attrs:{\"id\":\"maxThumbnails\",\"type\":\"number\",\"min\":\"0\",\"step\":\"1\"},domProps:{\"value\":(_vm.maxThumbnails)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.maxThumbnails=_vm._n($event.target.value)},\"blur\":function($event){_vm.$forceUpdate()}}})]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.hideNsfwLocal),expression:\"hideNsfwLocal\"}],attrs:{\"id\":\"hideNsfw\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.hideNsfwLocal)?_vm._i(_vm.hideNsfwLocal,null)>-1:(_vm.hideNsfwLocal)},on:{\"change\":function($event){var $$a=_vm.hideNsfwLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.hideNsfwLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.hideNsfwLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.hideNsfwLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"hideNsfw\"}},[_vm._v(_vm._s(_vm.$t('settings.nsfw_clickthrough')))])]),_vm._v(\" \"),_c('ul',{staticClass:\"setting-list suboptions\"},[_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.preloadImage),expression:\"preloadImage\"}],attrs:{\"id\":\"preloadImage\",\"disabled\":!_vm.hideNsfwLocal,\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.preloadImage)?_vm._i(_vm.preloadImage,null)>-1:(_vm.preloadImage)},on:{\"change\":function($event){var $$a=_vm.preloadImage,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.preloadImage=$$a.concat([$$v]))}else{$$i>-1&&(_vm.preloadImage=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.preloadImage=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"preloadImage\"}},[_vm._v(_vm._s(_vm.$t('settings.preload_images')))])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.useOneClickNsfw),expression:\"useOneClickNsfw\"}],attrs:{\"id\":\"useOneClickNsfw\",\"disabled\":!_vm.hideNsfwLocal,\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.useOneClickNsfw)?_vm._i(_vm.useOneClickNsfw,null)>-1:(_vm.useOneClickNsfw)},on:{\"change\":function($event){var $$a=_vm.useOneClickNsfw,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.useOneClickNsfw=$$a.concat([$$v]))}else{$$i>-1&&(_vm.useOneClickNsfw=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.useOneClickNsfw=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"useOneClickNsfw\"}},[_vm._v(_vm._s(_vm.$t('settings.use_one_click_nsfw')))])])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.stopGifs),expression:\"stopGifs\"}],attrs:{\"id\":\"stopGifs\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.stopGifs)?_vm._i(_vm.stopGifs,null)>-1:(_vm.stopGifs)},on:{\"change\":function($event){var $$a=_vm.stopGifs,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.stopGifs=$$a.concat([$$v]))}else{$$i>-1&&(_vm.stopGifs=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.stopGifs=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"stopGifs\"}},[_vm._v(_vm._s(_vm.$t('settings.stop_gifs')))])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.loopVideoLocal),expression:\"loopVideoLocal\"}],attrs:{\"id\":\"loopVideo\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.loopVideoLocal)?_vm._i(_vm.loopVideoLocal,null)>-1:(_vm.loopVideoLocal)},on:{\"change\":function($event){var $$a=_vm.loopVideoLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.loopVideoLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.loopVideoLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.loopVideoLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"loopVideo\"}},[_vm._v(_vm._s(_vm.$t('settings.loop_video')))]),_vm._v(\" \"),_c('ul',{staticClass:\"setting-list suboptions\",class:[{disabled: !_vm.streamingLocal}]},[_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.loopVideoSilentOnlyLocal),expression:\"loopVideoSilentOnlyLocal\"}],attrs:{\"id\":\"loopVideoSilentOnly\",\"disabled\":!_vm.loopVideoLocal || !_vm.loopSilentAvailable,\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.loopVideoSilentOnlyLocal)?_vm._i(_vm.loopVideoSilentOnlyLocal,null)>-1:(_vm.loopVideoSilentOnlyLocal)},on:{\"change\":function($event){var $$a=_vm.loopVideoSilentOnlyLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.loopVideoSilentOnlyLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.loopVideoSilentOnlyLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.loopVideoSilentOnlyLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"loopVideoSilentOnly\"}},[_vm._v(_vm._s(_vm.$t('settings.loop_video_silent_only')))]),_vm._v(\" \"),(!_vm.loopSilentAvailable)?_c('div',{staticClass:\"unavailable\"},[_c('i',{staticClass:\"icon-globe\"}),_vm._v(\"! \"+_vm._s(_vm.$t('settings.limited_availability'))+\"\\n \")]):_vm._e()])])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.playVideosInModal),expression:\"playVideosInModal\"}],attrs:{\"id\":\"playVideosInModal\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.playVideosInModal)?_vm._i(_vm.playVideosInModal,null)>-1:(_vm.playVideosInModal)},on:{\"change\":function($event){var $$a=_vm.playVideosInModal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.playVideosInModal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.playVideosInModal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.playVideosInModal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"playVideosInModal\"}},[_vm._v(_vm._s(_vm.$t('settings.play_videos_in_modal')))])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.useContainFit),expression:\"useContainFit\"}],attrs:{\"id\":\"useContainFit\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.useContainFit)?_vm._i(_vm.useContainFit,null)>-1:(_vm.useContainFit)},on:{\"change\":function($event){var $$a=_vm.useContainFit,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.useContainFit=$$a.concat([$$v]))}else{$$i>-1&&(_vm.useContainFit=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.useContainFit=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"useContainFit\"}},[_vm._v(_vm._s(_vm.$t('settings.use_contain_fit')))])])])]),_vm._v(\" \"),_c('div',{staticClass:\"setting-item\"},[_c('h2',[_vm._v(_vm._s(_vm.$t('settings.notifications')))]),_vm._v(\" \"),_c('ul',{staticClass:\"setting-list\"},[_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.webPushNotificationsLocal),expression:\"webPushNotificationsLocal\"}],attrs:{\"id\":\"webPushNotifications\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.webPushNotificationsLocal)?_vm._i(_vm.webPushNotificationsLocal,null)>-1:(_vm.webPushNotificationsLocal)},on:{\"change\":function($event){var $$a=_vm.webPushNotificationsLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.webPushNotificationsLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.webPushNotificationsLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.webPushNotificationsLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"webPushNotifications\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.enable_web_push_notifications'))+\"\\n \")])])])])]),_vm._v(\" \"),_c('div',{attrs:{\"label\":_vm.$t('settings.theme')}},[_c('div',{staticClass:\"setting-item\"},[_c('style-switcher')],1)]),_vm._v(\" \"),_c('div',{attrs:{\"label\":_vm.$t('settings.filtering')}},[_c('div',{staticClass:\"setting-item\"},[_c('div',{staticClass:\"select-multiple\"},[_c('span',{staticClass:\"label\"},[_vm._v(_vm._s(_vm.$t('settings.notification_visibility')))]),_vm._v(\" \"),_c('ul',{staticClass:\"option-list\"},[_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.notificationVisibilityLocal.likes),expression:\"notificationVisibilityLocal.likes\"}],attrs:{\"id\":\"notification-visibility-likes\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.notificationVisibilityLocal.likes)?_vm._i(_vm.notificationVisibilityLocal.likes,null)>-1:(_vm.notificationVisibilityLocal.likes)},on:{\"change\":function($event){var $$a=_vm.notificationVisibilityLocal.likes,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.$set(_vm.notificationVisibilityLocal, \"likes\", $$a.concat([$$v])))}else{$$i>-1&&(_vm.$set(_vm.notificationVisibilityLocal, \"likes\", $$a.slice(0,$$i).concat($$a.slice($$i+1))))}}else{_vm.$set(_vm.notificationVisibilityLocal, \"likes\", $$c)}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"notification-visibility-likes\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.notification_visibility_likes'))+\"\\n \")])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.notificationVisibilityLocal.repeats),expression:\"notificationVisibilityLocal.repeats\"}],attrs:{\"id\":\"notification-visibility-repeats\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.notificationVisibilityLocal.repeats)?_vm._i(_vm.notificationVisibilityLocal.repeats,null)>-1:(_vm.notificationVisibilityLocal.repeats)},on:{\"change\":function($event){var $$a=_vm.notificationVisibilityLocal.repeats,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.$set(_vm.notificationVisibilityLocal, \"repeats\", $$a.concat([$$v])))}else{$$i>-1&&(_vm.$set(_vm.notificationVisibilityLocal, \"repeats\", $$a.slice(0,$$i).concat($$a.slice($$i+1))))}}else{_vm.$set(_vm.notificationVisibilityLocal, \"repeats\", $$c)}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"notification-visibility-repeats\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.notification_visibility_repeats'))+\"\\n \")])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.notificationVisibilityLocal.follows),expression:\"notificationVisibilityLocal.follows\"}],attrs:{\"id\":\"notification-visibility-follows\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.notificationVisibilityLocal.follows)?_vm._i(_vm.notificationVisibilityLocal.follows,null)>-1:(_vm.notificationVisibilityLocal.follows)},on:{\"change\":function($event){var $$a=_vm.notificationVisibilityLocal.follows,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.$set(_vm.notificationVisibilityLocal, \"follows\", $$a.concat([$$v])))}else{$$i>-1&&(_vm.$set(_vm.notificationVisibilityLocal, \"follows\", $$a.slice(0,$$i).concat($$a.slice($$i+1))))}}else{_vm.$set(_vm.notificationVisibilityLocal, \"follows\", $$c)}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"notification-visibility-follows\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.notification_visibility_follows'))+\"\\n \")])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.notificationVisibilityLocal.mentions),expression:\"notificationVisibilityLocal.mentions\"}],attrs:{\"id\":\"notification-visibility-mentions\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.notificationVisibilityLocal.mentions)?_vm._i(_vm.notificationVisibilityLocal.mentions,null)>-1:(_vm.notificationVisibilityLocal.mentions)},on:{\"change\":function($event){var $$a=_vm.notificationVisibilityLocal.mentions,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.$set(_vm.notificationVisibilityLocal, \"mentions\", $$a.concat([$$v])))}else{$$i>-1&&(_vm.$set(_vm.notificationVisibilityLocal, \"mentions\", $$a.slice(0,$$i).concat($$a.slice($$i+1))))}}else{_vm.$set(_vm.notificationVisibilityLocal, \"mentions\", $$c)}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"notification-visibility-mentions\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.notification_visibility_mentions'))+\"\\n \")])])])]),_vm._v(\" \"),_c('div',[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.replies_in_timeline'))+\"\\n \"),_c('label',{staticClass:\"select\",attrs:{\"for\":\"replyVisibility\"}},[_c('select',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.replyVisibilityLocal),expression:\"replyVisibilityLocal\"}],attrs:{\"id\":\"replyVisibility\"},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.replyVisibilityLocal=$event.target.multiple ? $$selectedVal : $$selectedVal[0]}}},[_c('option',{attrs:{\"value\":\"all\",\"selected\":\"\"}},[_vm._v(_vm._s(_vm.$t('settings.reply_visibility_all')))]),_vm._v(\" \"),_c('option',{attrs:{\"value\":\"following\"}},[_vm._v(_vm._s(_vm.$t('settings.reply_visibility_following')))]),_vm._v(\" \"),_c('option',{attrs:{\"value\":\"self\"}},[_vm._v(_vm._s(_vm.$t('settings.reply_visibility_self')))])]),_vm._v(\" \"),_c('i',{staticClass:\"icon-down-open\"})])]),_vm._v(\" \"),_c('div',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.hidePostStatsLocal),expression:\"hidePostStatsLocal\"}],attrs:{\"id\":\"hidePostStats\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.hidePostStatsLocal)?_vm._i(_vm.hidePostStatsLocal,null)>-1:(_vm.hidePostStatsLocal)},on:{\"change\":function($event){var $$a=_vm.hidePostStatsLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.hidePostStatsLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.hidePostStatsLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.hidePostStatsLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"hidePostStats\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.hide_post_stats'))+\" \"+_vm._s(_vm.$t('settings.instance_default', { value: _vm.hidePostStatsDefault }))+\"\\n \")])]),_vm._v(\" \"),_c('div',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.hideUserStatsLocal),expression:\"hideUserStatsLocal\"}],attrs:{\"id\":\"hideUserStats\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.hideUserStatsLocal)?_vm._i(_vm.hideUserStatsLocal,null)>-1:(_vm.hideUserStatsLocal)},on:{\"change\":function($event){var $$a=_vm.hideUserStatsLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.hideUserStatsLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.hideUserStatsLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.hideUserStatsLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"hideUserStats\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.hide_user_stats'))+\" \"+_vm._s(_vm.$t('settings.instance_default', { value: _vm.hideUserStatsDefault }))+\"\\n \")])])]),_vm._v(\" \"),_c('div',{staticClass:\"setting-item\"},[_c('div',[_c('p',[_vm._v(_vm._s(_vm.$t('settings.filtering_explanation')))]),_vm._v(\" \"),_c('textarea',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.muteWordsString),expression:\"muteWordsString\"}],attrs:{\"id\":\"muteWords\"},domProps:{\"value\":(_vm.muteWordsString)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.muteWordsString=$event.target.value}}})]),_vm._v(\" \"),_c('div',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.hideFilteredStatusesLocal),expression:\"hideFilteredStatusesLocal\"}],attrs:{\"id\":\"hideFilteredStatuses\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.hideFilteredStatusesLocal)?_vm._i(_vm.hideFilteredStatusesLocal,null)>-1:(_vm.hideFilteredStatusesLocal)},on:{\"change\":function($event){var $$a=_vm.hideFilteredStatusesLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.hideFilteredStatusesLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.hideFilteredStatusesLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.hideFilteredStatusesLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"hideFilteredStatuses\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.hide_filtered_statuses'))+\" \"+_vm._s(_vm.$t('settings.instance_default', { value: _vm.hideFilteredStatusesDefault }))+\"\\n \")])])])]),_vm._v(\" \"),_c('div',{attrs:{\"label\":_vm.$t('settings.version.title')}},[_c('div',{staticClass:\"setting-item\"},[_c('ul',{staticClass:\"setting-list\"},[_c('li',[_c('p',[_vm._v(_vm._s(_vm.$t('settings.version.backend_version')))]),_vm._v(\" \"),_c('ul',{staticClass:\"option-list\"},[_c('li',[_c('a',{attrs:{\"href\":_vm.backendVersionLink,\"target\":\"_blank\"}},[_vm._v(_vm._s(_vm.backendVersion))])])])]),_vm._v(\" \"),_c('li',[_c('p',[_vm._v(_vm._s(_vm.$t('settings.version.frontend_version')))]),_vm._v(\" \"),_c('ul',{staticClass:\"option-list\"},[_c('li',[_c('a',{attrs:{\"href\":_vm.frontendVersionLink,\"target\":\"_blank\"}},[_vm._v(_vm._s(_vm.frontendVersion))])])])])])])])])],1)],1)])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"settings panel panel-default\"},[_c('div',{staticClass:\"panel-heading\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('registration.registration'))+\"\\n \")]),_vm._v(\" \"),_c('div',{staticClass:\"panel-body\"},[_c('form',{staticClass:\"registration-form\",on:{\"submit\":function($event){$event.preventDefault();_vm.submit(_vm.user)}}},[_c('div',{staticClass:\"container\"},[_c('div',{staticClass:\"text-fields\"},[_c('div',{staticClass:\"form-group\",class:{ 'form-group--error': _vm.$v.user.username.$error }},[_c('label',{staticClass:\"form--label\",attrs:{\"for\":\"sign-up-username\"}},[_vm._v(_vm._s(_vm.$t('login.username')))]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model.trim\",value:(_vm.$v.user.username.$model),expression:\"$v.user.username.$model\",modifiers:{\"trim\":true}}],staticClass:\"form-control\",attrs:{\"id\":\"sign-up-username\",\"disabled\":_vm.isPending,\"placeholder\":_vm.$t('registration.username_placeholder')},domProps:{\"value\":(_vm.$v.user.username.$model)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.$v.user.username, \"$model\", $event.target.value.trim())},\"blur\":function($event){_vm.$forceUpdate()}}})]),_vm._v(\" \"),(_vm.$v.user.username.$dirty)?_c('div',{staticClass:\"form-error\"},[_c('ul',[(!_vm.$v.user.username.required)?_c('li',[_c('span',[_vm._v(_vm._s(_vm.$t('registration.validations.username_required')))])]):_vm._e()])]):_vm._e(),_vm._v(\" \"),_c('div',{staticClass:\"form-group\",class:{ 'form-group--error': _vm.$v.user.fullname.$error }},[_c('label',{staticClass:\"form--label\",attrs:{\"for\":\"sign-up-fullname\"}},[_vm._v(_vm._s(_vm.$t('registration.fullname')))]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model.trim\",value:(_vm.$v.user.fullname.$model),expression:\"$v.user.fullname.$model\",modifiers:{\"trim\":true}}],staticClass:\"form-control\",attrs:{\"id\":\"sign-up-fullname\",\"disabled\":_vm.isPending,\"placeholder\":_vm.$t('registration.fullname_placeholder')},domProps:{\"value\":(_vm.$v.user.fullname.$model)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.$v.user.fullname, \"$model\", $event.target.value.trim())},\"blur\":function($event){_vm.$forceUpdate()}}})]),_vm._v(\" \"),(_vm.$v.user.fullname.$dirty)?_c('div',{staticClass:\"form-error\"},[_c('ul',[(!_vm.$v.user.fullname.required)?_c('li',[_c('span',[_vm._v(_vm._s(_vm.$t('registration.validations.fullname_required')))])]):_vm._e()])]):_vm._e(),_vm._v(\" \"),_c('div',{staticClass:\"form-group\",class:{ 'form-group--error': _vm.$v.user.email.$error }},[_c('label',{staticClass:\"form--label\",attrs:{\"for\":\"email\"}},[_vm._v(_vm._s(_vm.$t('registration.email')))]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.$v.user.email.$model),expression:\"$v.user.email.$model\"}],staticClass:\"form-control\",attrs:{\"id\":\"email\",\"disabled\":_vm.isPending,\"type\":\"email\"},domProps:{\"value\":(_vm.$v.user.email.$model)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.$v.user.email, \"$model\", $event.target.value)}}})]),_vm._v(\" \"),(_vm.$v.user.email.$dirty)?_c('div',{staticClass:\"form-error\"},[_c('ul',[(!_vm.$v.user.email.required)?_c('li',[_c('span',[_vm._v(_vm._s(_vm.$t('registration.validations.email_required')))])]):_vm._e()])]):_vm._e(),_vm._v(\" \"),_c('div',{staticClass:\"form-group\"},[_c('label',{staticClass:\"form--label\",attrs:{\"for\":\"bio\"}},[_vm._v(_vm._s(_vm.$t('registration.bio'))+\" (\"+_vm._s(_vm.$t('general.optional'))+\")\")]),_vm._v(\" \"),_c('textarea',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.user.bio),expression:\"user.bio\"}],staticClass:\"form-control\",attrs:{\"id\":\"bio\",\"disabled\":_vm.isPending,\"placeholder\":_vm.bioPlaceholder},domProps:{\"value\":(_vm.user.bio)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.user, \"bio\", $event.target.value)}}})]),_vm._v(\" \"),_c('div',{staticClass:\"form-group\",class:{ 'form-group--error': _vm.$v.user.password.$error }},[_c('label',{staticClass:\"form--label\",attrs:{\"for\":\"sign-up-password\"}},[_vm._v(_vm._s(_vm.$t('login.password')))]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.user.password),expression:\"user.password\"}],staticClass:\"form-control\",attrs:{\"id\":\"sign-up-password\",\"disabled\":_vm.isPending,\"type\":\"password\"},domProps:{\"value\":(_vm.user.password)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.user, \"password\", $event.target.value)}}})]),_vm._v(\" \"),(_vm.$v.user.password.$dirty)?_c('div',{staticClass:\"form-error\"},[_c('ul',[(!_vm.$v.user.password.required)?_c('li',[_c('span',[_vm._v(_vm._s(_vm.$t('registration.validations.password_required')))])]):_vm._e()])]):_vm._e(),_vm._v(\" \"),_c('div',{staticClass:\"form-group\",class:{ 'form-group--error': _vm.$v.user.confirm.$error }},[_c('label',{staticClass:\"form--label\",attrs:{\"for\":\"sign-up-password-confirmation\"}},[_vm._v(_vm._s(_vm.$t('registration.password_confirm')))]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.user.confirm),expression:\"user.confirm\"}],staticClass:\"form-control\",attrs:{\"id\":\"sign-up-password-confirmation\",\"disabled\":_vm.isPending,\"type\":\"password\"},domProps:{\"value\":(_vm.user.confirm)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.user, \"confirm\", $event.target.value)}}})]),_vm._v(\" \"),(_vm.$v.user.confirm.$dirty)?_c('div',{staticClass:\"form-error\"},[_c('ul',[(!_vm.$v.user.confirm.required)?_c('li',[_c('span',[_vm._v(_vm._s(_vm.$t('registration.validations.password_confirmation_required')))])]):_vm._e(),_vm._v(\" \"),(!_vm.$v.user.confirm.sameAsPassword)?_c('li',[_c('span',[_vm._v(_vm._s(_vm.$t('registration.validations.password_confirmation_match')))])]):_vm._e()])]):_vm._e(),_vm._v(\" \"),(_vm.captcha.type != 'none')?_c('div',{staticClass:\"form-group\",attrs:{\"id\":\"captcha-group\"}},[_c('label',{staticClass:\"form--label\",attrs:{\"for\":\"captcha-label\"}},[_vm._v(_vm._s(_vm.$t('captcha')))]),_vm._v(\" \"),(_vm.captcha.type == 'kocaptcha')?[_c('img',{attrs:{\"src\":_vm.captcha.url},on:{\"click\":_vm.setCaptcha}}),_vm._v(\" \"),_c('sub',[_vm._v(_vm._s(_vm.$t('registration.new_captcha')))]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.captcha.solution),expression:\"captcha.solution\"}],staticClass:\"form-control\",attrs:{\"id\":\"captcha-answer\",\"disabled\":_vm.isPending,\"type\":\"text\",\"autocomplete\":\"off\"},domProps:{\"value\":(_vm.captcha.solution)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.captcha, \"solution\", $event.target.value)}}})]:_vm._e()],2):_vm._e(),_vm._v(\" \"),(_vm.token)?_c('div',{staticClass:\"form-group\"},[_c('label',{attrs:{\"for\":\"token\"}},[_vm._v(_vm._s(_vm.$t('registration.token')))]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.token),expression:\"token\"}],staticClass:\"form-control\",attrs:{\"id\":\"token\",\"disabled\":\"true\",\"type\":\"text\"},domProps:{\"value\":(_vm.token)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.token=$event.target.value}}})]):_vm._e(),_vm._v(\" \"),_c('div',{staticClass:\"form-group\"},[_c('button',{staticClass:\"btn btn-default\",attrs:{\"disabled\":_vm.isPending,\"type\":\"submit\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.submit'))+\"\\n \")])])]),_vm._v(\" \"),_c('div',{staticClass:\"terms-of-service\",domProps:{\"innerHTML\":_vm._s(_vm.termsOfService)}})]),_vm._v(\" \"),(_vm.serverValidationErrors.length)?_c('div',{staticClass:\"form-group\"},[_c('div',{staticClass:\"alert error\"},_vm._l((_vm.serverValidationErrors),function(error){return _c('span',{key:error},[_vm._v(_vm._s(error))])}),0)]):_vm._e()])])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"settings panel panel-default\"},[_c('div',{staticClass:\"panel-heading\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('password_reset.password_reset'))+\"\\n \")]),_vm._v(\" \"),_c('div',{staticClass:\"panel-body\"},[_c('form',{staticClass:\"password-reset-form\",on:{\"submit\":function($event){$event.preventDefault();return _vm.submit($event)}}},[_c('div',{staticClass:\"container\"},[(!_vm.mailerEnabled)?_c('div',[_c('p',[_vm._v(\"\\n \"+_vm._s(_vm.$t('password_reset.password_reset_disabled'))+\"\\n \")])]):(_vm.success || _vm.throttled)?_c('div',[(_vm.success)?_c('p',[_vm._v(\"\\n \"+_vm._s(_vm.$t('password_reset.check_email'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),_c('div',{staticClass:\"form-group text-center\"},[_c('router-link',{attrs:{\"to\":{name: 'root'}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('password_reset.return_home'))+\"\\n \")])],1)]):_c('div',[_c('p',[_vm._v(\"\\n \"+_vm._s(_vm.$t('password_reset.instruction'))+\"\\n \")]),_vm._v(\" \"),_c('div',{staticClass:\"form-group\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.user.email),expression:\"user.email\"}],ref:\"email\",staticClass:\"form-control\",attrs:{\"disabled\":_vm.isPending,\"placeholder\":_vm.$t('password_reset.placeholder'),\"type\":\"input\"},domProps:{\"value\":(_vm.user.email)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.user, \"email\", $event.target.value)}}})]),_vm._v(\" \"),_c('div',{staticClass:\"form-group\"},[_c('button',{staticClass:\"btn btn-default btn-block\",attrs:{\"disabled\":_vm.isPending,\"type\":\"submit\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.submit'))+\"\\n \")])])]),_vm._v(\" \"),(_vm.error)?_c('p',{staticClass:\"alert error notice-dismissible\"},[_c('span',[_vm._v(_vm._s(_vm.error))]),_vm._v(\" \"),_c('a',{staticClass:\"button-icon dismiss\",on:{\"click\":function($event){$event.preventDefault();_vm.dismissError()}}},[_c('i',{staticClass:\"icon-cancel\"})])]):_vm._e()])])])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"image-cropper\"},[(_vm.dataUrl)?_c('div',[_c('div',{staticClass:\"image-cropper-image-container\"},[_c('img',{ref:\"img\",attrs:{\"src\":_vm.dataUrl,\"alt\":\"\"},on:{\"load\":function($event){$event.stopPropagation();return _vm.createCropper($event)}}})]),_vm._v(\" \"),_c('div',{staticClass:\"image-cropper-buttons-wrapper\"},[_c('button',{staticClass:\"btn\",attrs:{\"type\":\"button\",\"disabled\":_vm.submitting},domProps:{\"textContent\":_vm._s(_vm.saveText)},on:{\"click\":function($event){_vm.submit()}}}),_vm._v(\" \"),_c('button',{staticClass:\"btn\",attrs:{\"type\":\"button\",\"disabled\":_vm.submitting},domProps:{\"textContent\":_vm._s(_vm.cancelText)},on:{\"click\":_vm.destroy}}),_vm._v(\" \"),_c('button',{staticClass:\"btn\",attrs:{\"type\":\"button\",\"disabled\":_vm.submitting},domProps:{\"textContent\":_vm._s(_vm.saveWithoutCroppingText)},on:{\"click\":function($event){_vm.submit(false)}}}),_vm._v(\" \"),(_vm.submitting)?_c('i',{staticClass:\"icon-spin4 animate-spin\"}):_vm._e()]),_vm._v(\" \"),(_vm.submitError)?_c('div',{staticClass:\"alert error\"},[_vm._v(\"\\n \"+_vm._s(_vm.submitErrorMsg)+\"\\n \"),_c('i',{staticClass:\"button-icon icon-cancel\",on:{\"click\":_vm.clearError}})]):_vm._e()]):_vm._e(),_vm._v(\" \"),_c('input',{ref:\"input\",staticClass:\"image-cropper-img-input\",attrs:{\"type\":\"file\",\"accept\":_vm.mimes}})])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('basic-user-card',{attrs:{\"user\":_vm.user}},[_c('div',{staticClass:\"block-card-content-container\"},[(_vm.blocked)?_c('button',{staticClass:\"btn btn-default\",attrs:{\"disabled\":_vm.progress},on:{\"click\":_vm.unblockUser}},[(_vm.progress)?[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.unblock_progress'))+\"\\n \")]:[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.unblock'))+\"\\n \")]],2):_c('button',{staticClass:\"btn btn-default\",attrs:{\"disabled\":_vm.progress},on:{\"click\":_vm.blockUser}},[(_vm.progress)?[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.block_progress'))+\"\\n \")]:[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.block'))+\"\\n \")]],2)])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('basic-user-card',{attrs:{\"user\":_vm.user}},[_c('div',{staticClass:\"mute-card-content-container\"},[(_vm.muted)?_c('button',{staticClass:\"btn btn-default\",attrs:{\"disabled\":_vm.progress},on:{\"click\":_vm.unmuteUser}},[(_vm.progress)?[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.unmute_progress'))+\"\\n \")]:[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.unmute'))+\"\\n \")]],2):_c('button',{staticClass:\"btn btn-default\",attrs:{\"disabled\":_vm.progress},on:{\"click\":_vm.muteUser}},[(_vm.progress)?[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.mute_progress'))+\"\\n \")]:[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.mute'))+\"\\n \")]],2)])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('label',{staticClass:\"checkbox\"},[_c('input',{attrs:{\"type\":\"checkbox\"},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',[_vm._t(\"default\")],2):_vm._e()])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"selectable-list\"},[(_vm.items.length > 0)?_c('div',{staticClass:\"selectable-list-header\"},[_c('div',{staticClass:\"selectable-list-checkbox-wrapper\"},[_c('Checkbox',{attrs:{\"checked\":_vm.allSelected,\"indeterminate\":_vm.someSelected},on:{\"change\":_vm.toggleAll}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('selectable_list.select_all'))+\"\\n \")])],1),_vm._v(\" \"),_c('div',{staticClass:\"selectable-list-header-actions\"},[_vm._t(\"header\",null,{selected:_vm.filteredSelected})],2)]):_vm._e(),_vm._v(\" \"),_c('List',{attrs:{\"items\":_vm.items,\"get-key\":_vm.getKey},scopedSlots:_vm._u([{key:\"item\",fn:function(ref){\nvar item = ref.item;\nreturn [_c('div',{staticClass:\"selectable-list-item-inner\",class:{ 'selectable-list-item-selected-inner': _vm.isSelected(item) }},[_c('div',{staticClass:\"selectable-list-checkbox-wrapper\"},[_c('Checkbox',{attrs:{\"checked\":_vm.isSelected(item)},on:{\"change\":function (checked) { return _vm.toggle(checked, item); }}})],1),_vm._v(\" \"),_vm._t(\"item\",null,{item:item})],2)]}}])},[_c('template',{slot:\"empty\"},[_vm._t(\"empty\")],2)],2)],1)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{directives:[{name:\"click-outside\",rawName:\"v-click-outside\",value:(_vm.onClickOutside),expression:\"onClickOutside\"}],staticClass:\"autosuggest\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.term),expression:\"term\"}],staticClass:\"autosuggest-input\",attrs:{\"placeholder\":_vm.placeholder},domProps:{\"value\":(_vm.term)},on:{\"click\":_vm.onInputClick,\"input\":function($event){if($event.target.composing){ return; }_vm.term=$event.target.value}}}),_vm._v(\" \"),(_vm.resultsVisible && _vm.filtered.length > 0)?_c('div',{staticClass:\"autosuggest-results\"},[_vm._l((_vm.filtered),function(item){return _vm._t(\"default\",null,{item:item})})],2):_vm._e()])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"importer\"},[_c('form',[_c('input',{ref:\"input\",attrs:{\"type\":\"file\"},on:{\"change\":_vm.change}})]),_vm._v(\" \"),(_vm.submitting)?_c('i',{staticClass:\"icon-spin4 animate-spin importer-uploading\"}):_c('button',{staticClass:\"btn btn-default\",on:{\"click\":_vm.submit}},[_vm._v(\"\\n \"+_vm._s(_vm.submitButtonLabel)+\"\\n \")]),_vm._v(\" \"),(_vm.success)?_c('div',[_c('i',{staticClass:\"icon-cross\",on:{\"click\":_vm.dismiss}}),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.successMessage))])]):(_vm.error)?_c('div',[_c('i',{staticClass:\"icon-cross\",on:{\"click\":_vm.dismiss}}),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.errorMessage))])]):_vm._e()])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"exporter\"},[(_vm.processing)?_c('div',[_c('i',{staticClass:\"icon-spin4 animate-spin exporter-processing\"}),_vm._v(\" \"),_c('span',[_vm._v(_vm._s(_vm.processingMessage))])]):_c('button',{staticClass:\"btn btn-default\",on:{\"click\":_vm.process}},[_vm._v(\"\\n \"+_vm._s(_vm.exportButtonLabel)+\"\\n \")])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[(_vm.displayTitle)?_c('h4',[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.mfa.recovery_codes'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(_vm.inProgress)?_c('i',[_vm._v(_vm._s(_vm.$t('settings.mfa.waiting_a_recovery_codes')))]):_vm._e(),_vm._v(\" \"),(_vm.ready)?[_c('p',{staticClass:\"alert warning\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.mfa.recovery_codes_warning'))+\"\\n \")]),_vm._v(\" \"),_c('ul',{staticClass:\"backup-codes\"},_vm._l((_vm.backupCodes.codes),function(code){return _c('li',{key:code},[_vm._v(\"\\n \"+_vm._s(code)+\"\\n \")])}),0)]:_vm._e()],2)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_vm._t(\"default\"),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-default\",attrs:{\"disabled\":_vm.disabled},on:{\"click\":_vm.confirm}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.confirm'))+\"\\n \")]),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-default\",attrs:{\"disabled\":_vm.disabled},on:{\"click\":_vm.cancel}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.cancel'))+\"\\n \")])],2)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('div',{staticClass:\"method-item\"},[_c('strong',[_vm._v(_vm._s(_vm.$t('settings.mfa.otp')))]),_vm._v(\" \"),(!_vm.isActivated)?_c('button',{staticClass:\"btn btn-default\",on:{\"click\":_vm.doActivate}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.enable'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(_vm.isActivated)?_c('button',{staticClass:\"btn btn-default\",attrs:{\"disabled\":_vm.deactivate},on:{\"click\":_vm.doDeactivate}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.disable'))+\"\\n \")]):_vm._e()]),_vm._v(\" \"),(_vm.deactivate)?_c('confirm',{attrs:{\"disabled\":_vm.inProgress},on:{\"confirm\":_vm.confirmDeactivate,\"cancel\":_vm.cancelDeactivate}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.enter_current_password_to_confirm'))+\":\\n \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.currentPassword),expression:\"currentPassword\"}],attrs:{\"type\":\"password\"},domProps:{\"value\":(_vm.currentPassword)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.currentPassword=$event.target.value}}})]):_vm._e(),_vm._v(\" \"),(_vm.error)?_c('div',{staticClass:\"alert error\"},[_vm._v(\"\\n \"+_vm._s(_vm.error)+\"\\n \")]):_vm._e()],1)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return (_vm.readyInit && _vm.settings.available)?_c('div',{staticClass:\"setting-item mfa-settings\"},[_c('div',{staticClass:\"mfa-heading\"},[_c('h2',[_vm._v(_vm._s(_vm.$t('settings.mfa.title')))])]),_vm._v(\" \"),_c('div',[(!_vm.setupInProgress)?_c('div',{staticClass:\"setting-item\"},[_c('h3',[_vm._v(_vm._s(_vm.$t('settings.mfa.authentication_methods')))]),_vm._v(\" \"),_c('totp-item',{attrs:{\"settings\":_vm.settings},on:{\"deactivate\":_vm.fetchSettings,\"activate\":_vm.activateOTP}}),_vm._v(\" \"),_c('br'),_vm._v(\" \"),(_vm.settings.enabled)?_c('div',[(!_vm.confirmNewBackupCodes)?_c('recovery-codes',{attrs:{\"backup-codes\":_vm.backupCodes}}):_vm._e(),_vm._v(\" \"),(!_vm.confirmNewBackupCodes)?_c('button',{staticClass:\"btn btn-default\",on:{\"click\":_vm.getBackupCodes}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.mfa.generate_new_recovery_codes'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(_vm.confirmNewBackupCodes)?_c('div',[_c('confirm',{attrs:{\"disabled\":_vm.backupCodes.inProgress},on:{\"confirm\":_vm.confirmBackupCodes,\"cancel\":_vm.cancelBackupCodes}},[_c('p',{staticClass:\"warning\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.mfa.warning_of_generate_new_codes'))+\"\\n \")])])],1):_vm._e()],1):_vm._e()],1):_vm._e(),_vm._v(\" \"),(_vm.setupInProgress)?_c('div',[_c('h3',[_vm._v(_vm._s(_vm.$t('settings.mfa.setup_otp')))]),_vm._v(\" \"),(!_vm.setupOTPInProgress)?_c('recovery-codes',{attrs:{\"backup-codes\":_vm.backupCodes}}):_vm._e(),_vm._v(\" \"),(_vm.canSetupOTP)?_c('button',{staticClass:\"btn btn-default\",on:{\"click\":_vm.cancelSetup}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.cancel'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(_vm.canSetupOTP)?_c('button',{staticClass:\"btn btn-default\",on:{\"click\":_vm.setupOTP}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.mfa.setup_otp'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(_vm.setupOTPInProgress)?[(_vm.prepareOTP)?_c('i',[_vm._v(_vm._s(_vm.$t('settings.mfa.wait_pre_setup_otp')))]):_vm._e(),_vm._v(\" \"),(_vm.confirmOTP)?_c('div',[_c('div',{staticClass:\"setup-otp\"},[_c('div',{staticClass:\"qr-code\"},[_c('h4',[_vm._v(_vm._s(_vm.$t('settings.mfa.scan.title')))]),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.mfa.scan.desc')))]),_vm._v(\" \"),_c('qrcode',{attrs:{\"value\":_vm.otpSettings.provisioning_uri,\"options\":{ width: 200 }}}),_vm._v(\" \"),_c('p',[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.mfa.scan.secret_code'))+\":\\n \"+_vm._s(_vm.otpSettings.key)+\"\\n \")])],1),_vm._v(\" \"),_c('div',{staticClass:\"verify\"},[_c('h4',[_vm._v(_vm._s(_vm.$t('general.verify')))]),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.mfa.verify.desc')))]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.otpConfirmToken),expression:\"otpConfirmToken\"}],attrs:{\"type\":\"text\"},domProps:{\"value\":(_vm.otpConfirmToken)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.otpConfirmToken=$event.target.value}}}),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.enter_current_password_to_confirm'))+\":\")]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.currentPassword),expression:\"currentPassword\"}],attrs:{\"type\":\"password\"},domProps:{\"value\":(_vm.currentPassword)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.currentPassword=$event.target.value}}}),_vm._v(\" \"),_c('div',{staticClass:\"confirm-otp-actions\"},[_c('button',{staticClass:\"btn btn-default\",on:{\"click\":_vm.doConfirmOTP}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.mfa.confirm_and_enable'))+\"\\n \")]),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-default\",on:{\"click\":_vm.cancelSetup}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.cancel'))+\"\\n \")])]),_vm._v(\" \"),(_vm.error)?_c('div',{staticClass:\"alert error\"},[_vm._v(\"\\n \"+_vm._s(_vm.error)+\"\\n \")]):_vm._e()])])]):_vm._e()]:_vm._e()],2):_vm._e()])]):_vm._e()}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"settings panel panel-default\"},[_c('div',{staticClass:\"panel-heading\"},[_c('div',{staticClass:\"title\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.user_settings'))+\"\\n \")]),_vm._v(\" \"),_c('transition',{attrs:{\"name\":\"fade\"}},[(_vm.currentSaveStateNotice)?[(_vm.currentSaveStateNotice.error)?_c('div',{staticClass:\"alert error\",on:{\"click\":function($event){$event.preventDefault();}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.saving_err'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(!_vm.currentSaveStateNotice.error)?_c('div',{staticClass:\"alert transparent\",on:{\"click\":function($event){$event.preventDefault();}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.saving_ok'))+\"\\n \")]):_vm._e()]:_vm._e()],2)],1),_vm._v(\" \"),_c('div',{staticClass:\"panel-body profile-edit\"},[_c('tab-switcher',[_c('div',{attrs:{\"label\":_vm.$t('settings.profile_tab')}},[_c('div',{staticClass:\"setting-item\"},[_c('h2',[_vm._v(_vm._s(_vm.$t('settings.name_bio')))]),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.name')))]),_vm._v(\" \"),_c('EmojiInput',{attrs:{\"enable-emoji-picker\":\"\",\"suggest\":_vm.emojiSuggestor},model:{value:(_vm.newName),callback:function ($$v) {_vm.newName=$$v},expression:\"newName\"}},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.newName),expression:\"newName\"}],attrs:{\"id\":\"username\",\"classname\":\"name-changer\"},domProps:{\"value\":(_vm.newName)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.newName=$event.target.value}}})]),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.bio')))]),_vm._v(\" \"),_c('EmojiInput',{attrs:{\"enable-emoji-picker\":\"\",\"suggest\":_vm.emojiUserSuggestor},model:{value:(_vm.newBio),callback:function ($$v) {_vm.newBio=$$v},expression:\"newBio\"}},[_c('textarea',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.newBio),expression:\"newBio\"}],attrs:{\"classname\":\"bio\"},domProps:{\"value\":(_vm.newBio)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.newBio=$event.target.value}}})]),_vm._v(\" \"),_c('p',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.newLocked),expression:\"newLocked\"}],attrs:{\"id\":\"account-locked\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.newLocked)?_vm._i(_vm.newLocked,null)>-1:(_vm.newLocked)},on:{\"change\":function($event){var $$a=_vm.newLocked,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.newLocked=$$a.concat([$$v]))}else{$$i>-1&&(_vm.newLocked=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.newLocked=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"account-locked\"}},[_vm._v(_vm._s(_vm.$t('settings.lock_account_description')))])]),_vm._v(\" \"),_c('div',[_c('label',{attrs:{\"for\":\"default-vis\"}},[_vm._v(_vm._s(_vm.$t('settings.default_vis')))]),_vm._v(\" \"),_c('div',{staticClass:\"visibility-tray\",attrs:{\"id\":\"default-vis\"}},[_c('scope-selector',{attrs:{\"show-all\":true,\"user-default\":_vm.newDefaultScope,\"initial-scope\":_vm.newDefaultScope,\"on-scope-change\":_vm.changeVis}})],1)]),_vm._v(\" \"),_c('p',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.newNoRichText),expression:\"newNoRichText\"}],attrs:{\"id\":\"account-no-rich-text\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.newNoRichText)?_vm._i(_vm.newNoRichText,null)>-1:(_vm.newNoRichText)},on:{\"change\":function($event){var $$a=_vm.newNoRichText,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.newNoRichText=$$a.concat([$$v]))}else{$$i>-1&&(_vm.newNoRichText=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.newNoRichText=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"account-no-rich-text\"}},[_vm._v(_vm._s(_vm.$t('settings.no_rich_text_description')))])]),_vm._v(\" \"),_c('p',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.hideFollows),expression:\"hideFollows\"}],attrs:{\"id\":\"account-hide-follows\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.hideFollows)?_vm._i(_vm.hideFollows,null)>-1:(_vm.hideFollows)},on:{\"change\":function($event){var $$a=_vm.hideFollows,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.hideFollows=$$a.concat([$$v]))}else{$$i>-1&&(_vm.hideFollows=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.hideFollows=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"account-hide-follows\"}},[_vm._v(_vm._s(_vm.$t('settings.hide_follows_description')))])]),_vm._v(\" \"),_c('p',{staticClass:\"setting-subitem\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.hideFollowsCount),expression:\"hideFollowsCount\"}],attrs:{\"id\":\"account-hide-follows-count\",\"type\":\"checkbox\",\"disabled\":!_vm.hideFollows},domProps:{\"checked\":Array.isArray(_vm.hideFollowsCount)?_vm._i(_vm.hideFollowsCount,null)>-1:(_vm.hideFollowsCount)},on:{\"change\":function($event){var $$a=_vm.hideFollowsCount,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.hideFollowsCount=$$a.concat([$$v]))}else{$$i>-1&&(_vm.hideFollowsCount=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.hideFollowsCount=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"account-hide-follows-count\"}},[_vm._v(_vm._s(_vm.$t('settings.hide_follows_count_description')))])]),_vm._v(\" \"),_c('p',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.hideFollowers),expression:\"hideFollowers\"}],attrs:{\"id\":\"account-hide-followers\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.hideFollowers)?_vm._i(_vm.hideFollowers,null)>-1:(_vm.hideFollowers)},on:{\"change\":function($event){var $$a=_vm.hideFollowers,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.hideFollowers=$$a.concat([$$v]))}else{$$i>-1&&(_vm.hideFollowers=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.hideFollowers=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"account-hide-followers\"}},[_vm._v(_vm._s(_vm.$t('settings.hide_followers_description')))])]),_vm._v(\" \"),_c('p',{staticClass:\"setting-subitem\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.hideFollowersCount),expression:\"hideFollowersCount\"}],attrs:{\"id\":\"account-hide-followers-count\",\"type\":\"checkbox\",\"disabled\":!_vm.hideFollowers},domProps:{\"checked\":Array.isArray(_vm.hideFollowersCount)?_vm._i(_vm.hideFollowersCount,null)>-1:(_vm.hideFollowersCount)},on:{\"change\":function($event){var $$a=_vm.hideFollowersCount,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.hideFollowersCount=$$a.concat([$$v]))}else{$$i>-1&&(_vm.hideFollowersCount=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.hideFollowersCount=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"account-hide-followers-count\"}},[_vm._v(_vm._s(_vm.$t('settings.hide_followers_count_description')))])]),_vm._v(\" \"),_c('p',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.showRole),expression:\"showRole\"}],attrs:{\"id\":\"account-show-role\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.showRole)?_vm._i(_vm.showRole,null)>-1:(_vm.showRole)},on:{\"change\":function($event){var $$a=_vm.showRole,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.showRole=$$a.concat([$$v]))}else{$$i>-1&&(_vm.showRole=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.showRole=$$c}}}}),_vm._v(\" \"),(_vm.role === 'admin')?_c('label',{attrs:{\"for\":\"account-show-role\"}},[_vm._v(_vm._s(_vm.$t('settings.show_admin_badge')))]):_vm._e(),_vm._v(\" \"),(_vm.role === 'moderator')?_c('label',{attrs:{\"for\":\"account-show-role\"}},[_vm._v(_vm._s(_vm.$t('settings.show_moderator_badge')))]):_vm._e()]),_vm._v(\" \"),_c('p',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.discoverable),expression:\"discoverable\"}],attrs:{\"id\":\"discoverable\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.discoverable)?_vm._i(_vm.discoverable,null)>-1:(_vm.discoverable)},on:{\"change\":function($event){var $$a=_vm.discoverable,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.discoverable=$$a.concat([$$v]))}else{$$i>-1&&(_vm.discoverable=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.discoverable=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"discoverable\"}},[_vm._v(_vm._s(_vm.$t('settings.discoverable')))])]),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-default\",attrs:{\"disabled\":_vm.newName && _vm.newName.length === 0},on:{\"click\":_vm.updateProfile}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.submit'))+\"\\n \")])],1),_vm._v(\" \"),_c('div',{staticClass:\"setting-item\"},[_c('h2',[_vm._v(_vm._s(_vm.$t('settings.avatar')))]),_vm._v(\" \"),_c('p',{staticClass:\"visibility-notice\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.avatar_size_instruction'))+\"\\n \")]),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.current_avatar')))]),_vm._v(\" \"),_c('img',{staticClass:\"current-avatar\",attrs:{\"src\":_vm.user.profile_image_url_original}}),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.set_new_avatar')))]),_vm._v(\" \"),_c('button',{directives:[{name:\"show\",rawName:\"v-show\",value:(_vm.pickAvatarBtnVisible),expression:\"pickAvatarBtnVisible\"}],staticClass:\"btn\",attrs:{\"id\":\"pick-avatar\",\"type\":\"button\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.upload_a_photo'))+\"\\n \")]),_vm._v(\" \"),_c('image-cropper',{attrs:{\"trigger\":\"#pick-avatar\",\"submit-handler\":_vm.submitAvatar},on:{\"open\":function($event){_vm.pickAvatarBtnVisible=false},\"close\":function($event){_vm.pickAvatarBtnVisible=true}}})],1),_vm._v(\" \"),_c('div',{staticClass:\"setting-item\"},[_c('h2',[_vm._v(_vm._s(_vm.$t('settings.profile_banner')))]),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.current_profile_banner')))]),_vm._v(\" \"),_c('img',{staticClass:\"banner\",attrs:{\"src\":_vm.user.cover_photo}}),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.set_new_profile_banner')))]),_vm._v(\" \"),(_vm.bannerPreview)?_c('img',{staticClass:\"banner\",attrs:{\"src\":_vm.bannerPreview}}):_vm._e(),_vm._v(\" \"),_c('div',[_c('input',{attrs:{\"type\":\"file\"},on:{\"change\":function($event){_vm.uploadFile('banner', $event)}}})]),_vm._v(\" \"),(_vm.bannerUploading)?_c('i',{staticClass:\" icon-spin4 animate-spin uploading\"}):(_vm.bannerPreview)?_c('button',{staticClass:\"btn btn-default\",on:{\"click\":_vm.submitBanner}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.submit'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(_vm.bannerUploadError)?_c('div',{staticClass:\"alert error\"},[_vm._v(\"\\n Error: \"+_vm._s(_vm.bannerUploadError)+\"\\n \"),_c('i',{staticClass:\"button-icon icon-cancel\",on:{\"click\":function($event){_vm.clearUploadError('banner')}}})]):_vm._e()]),_vm._v(\" \"),_c('div',{staticClass:\"setting-item\"},[_c('h2',[_vm._v(_vm._s(_vm.$t('settings.profile_background')))]),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.set_new_profile_background')))]),_vm._v(\" \"),(_vm.backgroundPreview)?_c('img',{staticClass:\"bg\",attrs:{\"src\":_vm.backgroundPreview}}):_vm._e(),_vm._v(\" \"),_c('div',[_c('input',{attrs:{\"type\":\"file\"},on:{\"change\":function($event){_vm.uploadFile('background', $event)}}})]),_vm._v(\" \"),(_vm.backgroundUploading)?_c('i',{staticClass:\" icon-spin4 animate-spin uploading\"}):(_vm.backgroundPreview)?_c('button',{staticClass:\"btn btn-default\",on:{\"click\":_vm.submitBg}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.submit'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(_vm.backgroundUploadError)?_c('div',{staticClass:\"alert error\"},[_vm._v(\"\\n Error: \"+_vm._s(_vm.backgroundUploadError)+\"\\n \"),_c('i',{staticClass:\"button-icon icon-cancel\",on:{\"click\":function($event){_vm.clearUploadError('background')}}})]):_vm._e()])]),_vm._v(\" \"),_c('div',{attrs:{\"label\":_vm.$t('settings.security_tab')}},[_c('div',{staticClass:\"setting-item\"},[_c('h2',[_vm._v(_vm._s(_vm.$t('settings.change_password')))]),_vm._v(\" \"),_c('div',[_c('p',[_vm._v(_vm._s(_vm.$t('settings.current_password')))]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.changePasswordInputs[0]),expression:\"changePasswordInputs[0]\"}],attrs:{\"type\":\"password\"},domProps:{\"value\":(_vm.changePasswordInputs[0])},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.changePasswordInputs, 0, $event.target.value)}}})]),_vm._v(\" \"),_c('div',[_c('p',[_vm._v(_vm._s(_vm.$t('settings.new_password')))]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.changePasswordInputs[1]),expression:\"changePasswordInputs[1]\"}],attrs:{\"type\":\"password\"},domProps:{\"value\":(_vm.changePasswordInputs[1])},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.changePasswordInputs, 1, $event.target.value)}}})]),_vm._v(\" \"),_c('div',[_c('p',[_vm._v(_vm._s(_vm.$t('settings.confirm_new_password')))]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.changePasswordInputs[2]),expression:\"changePasswordInputs[2]\"}],attrs:{\"type\":\"password\"},domProps:{\"value\":(_vm.changePasswordInputs[2])},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.changePasswordInputs, 2, $event.target.value)}}})]),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-default\",on:{\"click\":_vm.changePassword}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.submit'))+\"\\n \")]),_vm._v(\" \"),(_vm.changedPassword)?_c('p',[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.changed_password'))+\"\\n \")]):(_vm.changePasswordError !== false)?_c('p',[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.change_password_error'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(_vm.changePasswordError)?_c('p',[_vm._v(\"\\n \"+_vm._s(_vm.changePasswordError)+\"\\n \")]):_vm._e()]),_vm._v(\" \"),_c('div',{staticClass:\"setting-item\"},[_c('h2',[_vm._v(_vm._s(_vm.$t('settings.oauth_tokens')))]),_vm._v(\" \"),_c('table',{staticClass:\"oauth-tokens\"},[_c('thead',[_c('tr',[_c('th',[_vm._v(_vm._s(_vm.$t('settings.app_name')))]),_vm._v(\" \"),_c('th',[_vm._v(_vm._s(_vm.$t('settings.valid_until')))]),_vm._v(\" \"),_c('th')])]),_vm._v(\" \"),_c('tbody',_vm._l((_vm.oauthTokens),function(oauthToken){return _c('tr',{key:oauthToken.id},[_c('td',[_vm._v(_vm._s(oauthToken.appName))]),_vm._v(\" \"),_c('td',[_vm._v(_vm._s(oauthToken.validUntil))]),_vm._v(\" \"),_c('td',{staticClass:\"actions\"},[_c('button',{staticClass:\"btn btn-default\",on:{\"click\":function($event){_vm.revokeToken(oauthToken.id)}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.revoke_token'))+\"\\n \")])])])}),0)])]),_vm._v(\" \"),_c('mfa'),_vm._v(\" \"),_c('div',{staticClass:\"setting-item\"},[_c('h2',[_vm._v(_vm._s(_vm.$t('settings.delete_account')))]),_vm._v(\" \"),(!_vm.deletingAccount)?_c('p',[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.delete_account_description'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(_vm.deletingAccount)?_c('div',[_c('p',[_vm._v(_vm._s(_vm.$t('settings.delete_account_instructions')))]),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('login.password')))]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.deleteAccountConfirmPasswordInput),expression:\"deleteAccountConfirmPasswordInput\"}],attrs:{\"type\":\"password\"},domProps:{\"value\":(_vm.deleteAccountConfirmPasswordInput)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.deleteAccountConfirmPasswordInput=$event.target.value}}}),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-default\",on:{\"click\":_vm.deleteAccount}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.delete_account'))+\"\\n \")])]):_vm._e(),_vm._v(\" \"),(_vm.deleteAccountError !== false)?_c('p',[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.delete_account_error'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(_vm.deleteAccountError)?_c('p',[_vm._v(\"\\n \"+_vm._s(_vm.deleteAccountError)+\"\\n \")]):_vm._e(),_vm._v(\" \"),(!_vm.deletingAccount)?_c('button',{staticClass:\"btn btn-default\",on:{\"click\":_vm.confirmDelete}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.submit'))+\"\\n \")]):_vm._e()])],1),_vm._v(\" \"),(_vm.pleromaBackend)?_c('div',{attrs:{\"label\":_vm.$t('settings.notifications')}},[_c('div',{staticClass:\"setting-item\"},[_c('div',{staticClass:\"select-multiple\"},[_c('span',{staticClass:\"label\"},[_vm._v(_vm._s(_vm.$t('settings.notification_setting')))]),_vm._v(\" \"),_c('ul',{staticClass:\"option-list\"},[_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.notificationSettings.follows),expression:\"notificationSettings.follows\"}],attrs:{\"id\":\"notification-setting-follows\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.notificationSettings.follows)?_vm._i(_vm.notificationSettings.follows,null)>-1:(_vm.notificationSettings.follows)},on:{\"change\":function($event){var $$a=_vm.notificationSettings.follows,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.$set(_vm.notificationSettings, \"follows\", $$a.concat([$$v])))}else{$$i>-1&&(_vm.$set(_vm.notificationSettings, \"follows\", $$a.slice(0,$$i).concat($$a.slice($$i+1))))}}else{_vm.$set(_vm.notificationSettings, \"follows\", $$c)}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"notification-setting-follows\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.notification_setting_follows'))+\"\\n \")])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.notificationSettings.followers),expression:\"notificationSettings.followers\"}],attrs:{\"id\":\"notification-setting-followers\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.notificationSettings.followers)?_vm._i(_vm.notificationSettings.followers,null)>-1:(_vm.notificationSettings.followers)},on:{\"change\":function($event){var $$a=_vm.notificationSettings.followers,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.$set(_vm.notificationSettings, \"followers\", $$a.concat([$$v])))}else{$$i>-1&&(_vm.$set(_vm.notificationSettings, \"followers\", $$a.slice(0,$$i).concat($$a.slice($$i+1))))}}else{_vm.$set(_vm.notificationSettings, \"followers\", $$c)}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"notification-setting-followers\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.notification_setting_followers'))+\"\\n \")])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.notificationSettings.non_follows),expression:\"notificationSettings.non_follows\"}],attrs:{\"id\":\"notification-setting-non-follows\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.notificationSettings.non_follows)?_vm._i(_vm.notificationSettings.non_follows,null)>-1:(_vm.notificationSettings.non_follows)},on:{\"change\":function($event){var $$a=_vm.notificationSettings.non_follows,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.$set(_vm.notificationSettings, \"non_follows\", $$a.concat([$$v])))}else{$$i>-1&&(_vm.$set(_vm.notificationSettings, \"non_follows\", $$a.slice(0,$$i).concat($$a.slice($$i+1))))}}else{_vm.$set(_vm.notificationSettings, \"non_follows\", $$c)}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"notification-setting-non-follows\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.notification_setting_non_follows'))+\"\\n \")])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.notificationSettings.non_followers),expression:\"notificationSettings.non_followers\"}],attrs:{\"id\":\"notification-setting-non-followers\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.notificationSettings.non_followers)?_vm._i(_vm.notificationSettings.non_followers,null)>-1:(_vm.notificationSettings.non_followers)},on:{\"change\":function($event){var $$a=_vm.notificationSettings.non_followers,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.$set(_vm.notificationSettings, \"non_followers\", $$a.concat([$$v])))}else{$$i>-1&&(_vm.$set(_vm.notificationSettings, \"non_followers\", $$a.slice(0,$$i).concat($$a.slice($$i+1))))}}else{_vm.$set(_vm.notificationSettings, \"non_followers\", $$c)}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"notification-setting-non-followers\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.notification_setting_non_followers'))+\"\\n \")])])])]),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.notification_mutes')))]),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.notification_blocks')))]),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-default\",on:{\"click\":_vm.updateNotificationSettings}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.submit'))+\"\\n \")])])]):_vm._e(),_vm._v(\" \"),(_vm.pleromaBackend)?_c('div',{attrs:{\"label\":_vm.$t('settings.data_import_export_tab')}},[_c('div',{staticClass:\"setting-item\"},[_c('h2',[_vm._v(_vm._s(_vm.$t('settings.follow_import')))]),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.import_followers_from_a_csv_file')))]),_vm._v(\" \"),_c('Importer',{attrs:{\"submit-handler\":_vm.importFollows,\"success-message\":_vm.$t('settings.follows_imported'),\"error-message\":_vm.$t('settings.follow_import_error')}})],1),_vm._v(\" \"),_c('div',{staticClass:\"setting-item\"},[_c('h2',[_vm._v(_vm._s(_vm.$t('settings.follow_export')))]),_vm._v(\" \"),_c('Exporter',{attrs:{\"get-content\":_vm.getFollowsContent,\"filename\":\"friends.csv\",\"export-button-label\":_vm.$t('settings.follow_export_button')}})],1),_vm._v(\" \"),_c('div',{staticClass:\"setting-item\"},[_c('h2',[_vm._v(_vm._s(_vm.$t('settings.block_import')))]),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.import_blocks_from_a_csv_file')))]),_vm._v(\" \"),_c('Importer',{attrs:{\"submit-handler\":_vm.importBlocks,\"success-message\":_vm.$t('settings.blocks_imported'),\"error-message\":_vm.$t('settings.block_import_error')}})],1),_vm._v(\" \"),_c('div',{staticClass:\"setting-item\"},[_c('h2',[_vm._v(_vm._s(_vm.$t('settings.block_export')))]),_vm._v(\" \"),_c('Exporter',{attrs:{\"get-content\":_vm.getBlocksContent,\"filename\":\"blocks.csv\",\"export-button-label\":_vm.$t('settings.block_export_button')}})],1)]):_vm._e(),_vm._v(\" \"),_c('div',{attrs:{\"label\":_vm.$t('settings.blocks_tab')}},[_c('div',{staticClass:\"profile-edit-usersearch-wrapper\"},[_c('Autosuggest',{attrs:{\"filter\":_vm.filterUnblockedUsers,\"query\":_vm.queryUserIds,\"placeholder\":_vm.$t('settings.search_user_to_block')},scopedSlots:_vm._u([{key:\"default\",fn:function(row){return _c('BlockCard',{attrs:{\"user-id\":row.item}})}}])})],1),_vm._v(\" \"),_c('BlockList',{attrs:{\"refresh\":true,\"get-key\":_vm.identity},scopedSlots:_vm._u([{key:\"header\",fn:function(ref){\nvar selected = ref.selected;\nreturn [_c('div',{staticClass:\"profile-edit-bulk-actions\"},[(selected.length > 0)?_c('ProgressButton',{staticClass:\"btn btn-default\",attrs:{\"click\":function () { return _vm.blockUsers(selected); }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.block'))+\"\\n \"),_c('template',{slot:\"progress\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.block_progress'))+\"\\n \")])],2):_vm._e(),_vm._v(\" \"),(selected.length > 0)?_c('ProgressButton',{staticClass:\"btn btn-default\",attrs:{\"click\":function () { return _vm.unblockUsers(selected); }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.unblock'))+\"\\n \"),_c('template',{slot:\"progress\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.unblock_progress'))+\"\\n \")])],2):_vm._e()],1)]}},{key:\"item\",fn:function(ref){\nvar item = ref.item;\nreturn [_c('BlockCard',{attrs:{\"user-id\":item}})]}}])},[_c('template',{slot:\"empty\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.no_blocks'))+\"\\n \")])],2)],1),_vm._v(\" \"),_c('div',{attrs:{\"label\":_vm.$t('settings.mutes_tab')}},[_c('div',{staticClass:\"profile-edit-usersearch-wrapper\"},[_c('Autosuggest',{attrs:{\"filter\":_vm.filterUnMutedUsers,\"query\":_vm.queryUserIds,\"placeholder\":_vm.$t('settings.search_user_to_mute')},scopedSlots:_vm._u([{key:\"default\",fn:function(row){return _c('MuteCard',{attrs:{\"user-id\":row.item}})}}])})],1),_vm._v(\" \"),_c('MuteList',{attrs:{\"refresh\":true,\"get-key\":_vm.identity},scopedSlots:_vm._u([{key:\"header\",fn:function(ref){\nvar selected = ref.selected;\nreturn [_c('div',{staticClass:\"profile-edit-bulk-actions\"},[(selected.length > 0)?_c('ProgressButton',{staticClass:\"btn btn-default\",attrs:{\"click\":function () { return _vm.muteUsers(selected); }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.mute'))+\"\\n \"),_c('template',{slot:\"progress\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.mute_progress'))+\"\\n \")])],2):_vm._e(),_vm._v(\" \"),(selected.length > 0)?_c('ProgressButton',{staticClass:\"btn btn-default\",attrs:{\"click\":function () { return _vm.unmuteUsers(selected); }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.unmute'))+\"\\n \"),_c('template',{slot:\"progress\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.unmute_progress'))+\"\\n \")])],2):_vm._e()],1)]}},{key:\"item\",fn:function(ref){\nvar item = ref.item;\nreturn [_c('MuteCard',{attrs:{\"user-id\":item}})]}}])},[_c('template',{slot:\"empty\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.no_mutes'))+\"\\n \")])],2)],1)])],1)])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('basic-user-card',{attrs:{\"user\":_vm.user}},[_c('div',{staticClass:\"follow-request-card-content-container\"},[_c('button',{staticClass:\"btn btn-default\",on:{\"click\":_vm.approveUser}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.approve'))+\"\\n \")]),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-default\",on:{\"click\":_vm.denyUser}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.deny'))+\"\\n \")])])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"settings panel panel-default\"},[_c('div',{staticClass:\"panel-heading\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('nav.friend_requests'))+\"\\n \")]),_vm._v(\" \"),_c('div',{staticClass:\"panel-body\"},_vm._l((_vm.requests),function(request){return _c('FollowRequestCard',{key:request.id,staticClass:\"list-item\",attrs:{\"user\":request}})}),1)])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('h1',[_vm._v(\"...\")])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"login panel panel-default\"},[_c('div',{staticClass:\"panel-heading\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('login.login'))+\"\\n \")]),_vm._v(\" \"),_c('div',{staticClass:\"panel-body\"},[_c('form',{staticClass:\"login-form\",on:{\"submit\":function($event){$event.preventDefault();return _vm.submit($event)}}},[(_vm.isPasswordAuth)?[_c('div',{staticClass:\"form-group\"},[_c('label',{attrs:{\"for\":\"username\"}},[_vm._v(_vm._s(_vm.$t('login.username')))]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.user.username),expression:\"user.username\"}],staticClass:\"form-control\",attrs:{\"id\":\"username\",\"disabled\":_vm.loggingIn,\"placeholder\":_vm.$t('login.placeholder')},domProps:{\"value\":(_vm.user.username)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.user, \"username\", $event.target.value)}}})]),_vm._v(\" \"),_c('div',{staticClass:\"form-group\"},[_c('label',{attrs:{\"for\":\"password\"}},[_vm._v(_vm._s(_vm.$t('login.password')))]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.user.password),expression:\"user.password\"}],ref:\"passwordInput\",staticClass:\"form-control\",attrs:{\"id\":\"password\",\"disabled\":_vm.loggingIn,\"type\":\"password\"},domProps:{\"value\":(_vm.user.password)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.user, \"password\", $event.target.value)}}})]),_vm._v(\" \"),_c('div',{staticClass:\"form-group\"},[_c('router-link',{attrs:{\"to\":{name: 'password-reset'}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('password_reset.forgot_password'))+\"\\n \")])],1)]:_vm._e(),_vm._v(\" \"),(_vm.isTokenAuth)?_c('div',{staticClass:\"form-group\"},[_c('p',[_vm._v(_vm._s(_vm.$t('login.description')))])]):_vm._e(),_vm._v(\" \"),_c('div',{staticClass:\"form-group\"},[_c('div',{staticClass:\"login-bottom\"},[_c('div',[(_vm.registrationOpen)?_c('router-link',{staticClass:\"register\",attrs:{\"to\":{name: 'registration'}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('login.register'))+\"\\n \")]):_vm._e()],1),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-default\",attrs:{\"disabled\":_vm.loggingIn,\"type\":\"submit\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('login.login'))+\"\\n \")])])])],2)]),_vm._v(\" \"),(_vm.error)?_c('div',{staticClass:\"form-group\"},[_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()])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"login panel panel-default\"},[_c('div',{staticClass:\"panel-heading\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('login.heading.recovery'))+\"\\n \")]),_vm._v(\" \"),_c('div',{staticClass:\"panel-body\"},[_c('form',{staticClass:\"login-form\",on:{\"submit\":function($event){$event.preventDefault();return _vm.submit($event)}}},[_c('div',{staticClass:\"form-group\"},[_c('label',{attrs:{\"for\":\"code\"}},[_vm._v(_vm._s(_vm.$t('login.recovery_code')))]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.code),expression:\"code\"}],staticClass:\"form-control\",attrs:{\"id\":\"code\"},domProps:{\"value\":(_vm.code)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.code=$event.target.value}}})]),_vm._v(\" \"),_c('div',{staticClass:\"form-group\"},[_c('div',{staticClass:\"login-bottom\"},[_c('div',[_c('a',{attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();return _vm.requireTOTP($event)}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('login.enter_two_factor_code'))+\"\\n \")]),_vm._v(\" \"),_c('br'),_vm._v(\" \"),_c('a',{attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();return _vm.abortMFA($event)}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.cancel'))+\"\\n \")])]),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-default\",attrs:{\"type\":\"submit\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.verify'))+\"\\n \")])])])])]),_vm._v(\" \"),(_vm.error)?_c('div',{staticClass:\"form-group\"},[_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()])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"login panel panel-default\"},[_c('div',{staticClass:\"panel-heading\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('login.heading.totp'))+\"\\n \")]),_vm._v(\" \"),_c('div',{staticClass:\"panel-body\"},[_c('form',{staticClass:\"login-form\",on:{\"submit\":function($event){$event.preventDefault();return _vm.submit($event)}}},[_c('div',{staticClass:\"form-group\"},[_c('label',{attrs:{\"for\":\"code\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('login.authentication_code'))+\"\\n \")]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.code),expression:\"code\"}],staticClass:\"form-control\",attrs:{\"id\":\"code\"},domProps:{\"value\":(_vm.code)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.code=$event.target.value}}})]),_vm._v(\" \"),_c('div',{staticClass:\"form-group\"},[_c('div',{staticClass:\"login-bottom\"},[_c('div',[_c('a',{attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();return _vm.requireRecovery($event)}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('login.enter_recovery_code'))+\"\\n \")]),_vm._v(\" \"),_c('br'),_vm._v(\" \"),_c('a',{attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();return _vm.abortMFA($event)}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.cancel'))+\"\\n \")])]),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-default\",attrs:{\"type\":\"submit\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.verify'))+\"\\n \")])])])])]),_vm._v(\" \"),(_vm.error)?_c('div',{staticClass:\"form-group\"},[_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()])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return (!_vm.collapsed || !_vm.floating)?_c('div',{staticClass:\"chat-panel\"},[_c('div',{staticClass:\"panel panel-default\"},[_c('div',{staticClass:\"panel-heading timeline-heading\",class:{ 'chat-heading': _vm.floating },on:{\"click\":function($event){$event.stopPropagation();$event.preventDefault();return _vm.togglePanel($event)}}},[_c('div',{staticClass:\"title\"},[_c('span',[_vm._v(_vm._s(_vm.$t('chat.title')))]),_vm._v(\" \"),(_vm.floating)?_c('i',{staticClass:\"icon-cancel\"}):_vm._e()])]),_vm._v(\" \"),_c('div',{directives:[{name:\"chat-scroll\",rawName:\"v-chat-scroll\"}],staticClass:\"chat-window\"},_vm._l((_vm.messages),function(message){return _c('div',{key:message.id,staticClass:\"chat-message\"},[_c('span',{staticClass:\"chat-avatar\"},[_c('img',{attrs:{\"src\":message.author.avatar}})]),_vm._v(\" \"),_c('div',{staticClass:\"chat-content\"},[_c('router-link',{staticClass:\"chat-name\",attrs:{\"to\":_vm.userProfileLink(message.author)}},[_vm._v(\"\\n \"+_vm._s(message.author.username)+\"\\n \")]),_vm._v(\" \"),_c('br'),_vm._v(\" \"),_c('span',{staticClass:\"chat-text\"},[_vm._v(\"\\n \"+_vm._s(message.text)+\"\\n \")])],1)])}),0),_vm._v(\" \"),_c('div',{staticClass:\"chat-input\"},[_c('textarea',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.currentMessage),expression:\"currentMessage\"}],staticClass:\"chat-input-textarea\",attrs:{\"rows\":\"1\"},domProps:{\"value\":(_vm.currentMessage)},on:{\"keyup\":function($event){if(!('button' in $event)&&_vm._k($event.keyCode,\"enter\",13,$event.key,\"Enter\")){ return null; }_vm.submit(_vm.currentMessage)},\"input\":function($event){if($event.target.composing){ return; }_vm.currentMessage=$event.target.value}}})])])]):_c('div',{staticClass:\"chat-panel\"},[_c('div',{staticClass:\"panel panel-default\"},[_c('div',{staticClass:\"panel-heading stub timeline-heading chat-heading\",on:{\"click\":function($event){$event.stopPropagation();$event.preventDefault();return _vm.togglePanel($event)}}},[_c('div',{staticClass:\"title\"},[_c('i',{staticClass:\"icon-comment-empty\"}),_vm._v(\"\\n \"+_vm._s(_vm.$t('chat.title'))+\"\\n \")])])])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"panel panel-default\"},[_c('div',{staticClass:\"panel-heading\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('who_to_follow.who_to_follow'))+\"\\n \")]),_vm._v(\" \"),_c('div',{staticClass:\"panel-body\"},_vm._l((_vm.users),function(user){return _c('FollowCard',{key:user.id,staticClass:\"list-item\",attrs:{\"user\":user}})}),1)])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"instance-specific-panel\"},[_c('div',{staticClass:\"panel panel-default\"},[_c('div',{staticClass:\"panel-body\"},[_c('div',{domProps:{\"innerHTML\":_vm._s(_vm.instanceSpecificPanelContent)}})])])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"features-panel\"},[_c('div',{staticClass:\"panel panel-default base01-background\"},[_c('div',{staticClass:\"panel-heading timeline-heading base02-background base04\"},[_c('div',{staticClass:\"title\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('features_panel.title'))+\"\\n \")])]),_vm._v(\" \"),_c('div',{staticClass:\"panel-body features-panel\"},[_c('ul',[(_vm.chat)?_c('li',[_vm._v(\"\\n \"+_vm._s(_vm.$t('features_panel.chat'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(_vm.gopher)?_c('li',[_vm._v(\"\\n \"+_vm._s(_vm.$t('features_panel.gopher'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(_vm.whoToFollow)?_c('li',[_vm._v(\"\\n \"+_vm._s(_vm.$t('features_panel.who_to_follow'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(_vm.mediaProxy)?_c('li',[_vm._v(\"\\n \"+_vm._s(_vm.$t('features_panel.media_proxy'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),_c('li',[_vm._v(_vm._s(_vm.$t('features_panel.scope_options')))]),_vm._v(\" \"),_c('li',[_vm._v(_vm._s(_vm.$t('features_panel.text_limit'))+\" = \"+_vm._s(_vm.textlimit))])])])])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('div',{staticClass:\"panel panel-default\"},[_c('div',{staticClass:\"panel-body\"},[_c('div',{staticClass:\"tos-content\",domProps:{\"innerHTML\":_vm._s(_vm.content)}})])])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"sidebar\"},[_c('instance-specific-panel'),_vm._v(\" \"),(_vm.showFeaturesPanel)?_c('features-panel'):_vm._e(),_vm._v(\" \"),_c('terms-of-service-panel')],1)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"user-panel\"},[(_vm.signedIn)?_c('div',{key:\"user-panel\",staticClass:\"panel panel-default signed-in\"},[_c('UserCard',{attrs:{\"user\":_vm.user,\"hide-bio\":true,\"rounded\":\"top\"}}),_vm._v(\" \"),_c('div',{staticClass:\"panel-footer\"},[_c('PostStatusForm')],1)],1):_c('auth-form',{key:\"user-panel\"})],1)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"nav-panel\"},[_c('div',{staticClass:\"panel panel-default\"},[_c('ul',[(_vm.currentUser)?_c('li',[_c('router-link',{attrs:{\"to\":{ name: 'friends' }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"nav.timeline\"))+\"\\n \")])],1):_vm._e(),_vm._v(\" \"),(_vm.currentUser)?_c('li',[_c('router-link',{attrs:{\"to\":{ name: 'interactions', params: { username: _vm.currentUser.screen_name } }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"nav.interactions\"))+\"\\n \")])],1):_vm._e(),_vm._v(\" \"),(_vm.currentUser)?_c('li',[_c('router-link',{attrs:{\"to\":{ name: 'dms', params: { username: _vm.currentUser.screen_name } }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"nav.dms\"))+\"\\n \")])],1):_vm._e(),_vm._v(\" \"),(_vm.currentUser && _vm.currentUser.locked)?_c('li',[_c('router-link',{attrs:{\"to\":{ name: 'friend-requests' }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"nav.friend_requests\"))+\"\\n \"),(_vm.followRequestCount > 0)?_c('span',{staticClass:\"badge follow-request-count\"},[_vm._v(\"\\n \"+_vm._s(_vm.followRequestCount)+\"\\n \")]):_vm._e()])],1):_vm._e(),_vm._v(\" \"),_c('li',[_c('router-link',{attrs:{\"to\":{ name: 'public-timeline' }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"nav.public_tl\"))+\"\\n \")])],1),_vm._v(\" \"),_c('li',[_c('router-link',{attrs:{\"to\":{ name: 'public-external-timeline' }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"nav.twkn\"))+\"\\n \")])],1)])])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('div',{staticClass:\"search-bar-container\"},[(_vm.loading)?_c('i',{staticClass:\"icon-spin4 finder-icon animate-spin-slow\"}):_vm._e(),_vm._v(\" \"),(_vm.hidden)?_c('a',{attrs:{\"href\":\"#\",\"title\":_vm.$t('nav.search')}},[_c('i',{staticClass:\"button-icon icon-search\",on:{\"click\":function($event){$event.preventDefault();$event.stopPropagation();return _vm.toggleHidden($event)}}})]):[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.searchTerm),expression:\"searchTerm\"}],ref:\"searchInput\",staticClass:\"search-bar-input\",attrs:{\"id\":\"search-bar-input\",\"placeholder\":_vm.$t('nav.search'),\"type\":\"text\"},domProps:{\"value\":(_vm.searchTerm)},on:{\"keyup\":function($event){if(!('button' in $event)&&_vm._k($event.keyCode,\"enter\",13,$event.key,\"Enter\")){ return null; }_vm.find(_vm.searchTerm)},\"input\":function($event){if($event.target.composing){ return; }_vm.searchTerm=$event.target.value}}}),_vm._v(\" \"),_c('button',{staticClass:\"btn search-button\",on:{\"click\":function($event){_vm.find(_vm.searchTerm)}}},[_c('i',{staticClass:\"icon-search\"})]),_vm._v(\" \"),_c('i',{staticClass:\"button-icon icon-cancel\",on:{\"click\":function($event){$event.preventDefault();$event.stopPropagation();return _vm.toggleHidden($event)}}})]],2)])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"who-to-follow-panel\"},[_c('div',{staticClass:\"panel panel-default base01-background\"},[_c('div',{staticClass:\"panel-heading timeline-heading base02-background base04\"},[_c('div',{staticClass:\"title\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('who_to_follow.who_to_follow'))+\"\\n \")])]),_vm._v(\" \"),_c('div',{staticClass:\"who-to-follow\"},[_vm._l((_vm.usersToFollow),function(user){return _c('p',{key:user.id,staticClass:\"who-to-follow-items\"},[_c('img',{attrs:{\"src\":user.img}}),_vm._v(\" \"),_c('router-link',{attrs:{\"to\":_vm.userProfileLink(user.id, user.name)}},[_vm._v(\"\\n \"+_vm._s(user.name)+\"\\n \")]),_c('br')],1)}),_vm._v(\" \"),_c('p',{staticClass:\"who-to-follow-more\"},[_c('router-link',{attrs:{\"to\":{ name: 'who-to-follow' }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('who_to_follow.more'))+\"\\n \")])],1)],2)])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return (_vm.showing)?_c('div',{directives:[{name:\"body-scroll-lock\",rawName:\"v-body-scroll-lock\",value:(_vm.showing),expression:\"showing\"}],staticClass:\"modal-view media-modal-view\",on:{\"click\":function($event){$event.preventDefault();return _vm.hide($event)}}},[(_vm.type === 'image')?_c('img',{staticClass:\"modal-image\",attrs:{\"src\":_vm.currentMedia.url}}):_vm._e(),_vm._v(\" \"),(_vm.type === 'video')?_c('VideoAttachment',{staticClass:\"modal-image\",attrs:{\"attachment\":_vm.currentMedia,\"controls\":true},nativeOn:{\"click\":function($event){$event.stopPropagation();}}}):_vm._e(),_vm._v(\" \"),(_vm.canNavigate)?_c('button',{staticClass:\"modal-view-button-arrow modal-view-button-arrow--prev\",attrs:{\"title\":_vm.$t('media_modal.previous')},on:{\"click\":function($event){$event.stopPropagation();$event.preventDefault();return _vm.goPrev($event)}}},[_c('i',{staticClass:\"icon-left-open arrow-icon\"})]):_vm._e(),_vm._v(\" \"),(_vm.canNavigate)?_c('button',{staticClass:\"modal-view-button-arrow modal-view-button-arrow--next\",attrs:{\"title\":_vm.$t('media_modal.next')},on:{\"click\":function($event){$event.stopPropagation();$event.preventDefault();return _vm.goNext($event)}}},[_c('i',{staticClass:\"icon-right-open arrow-icon\"})]):_vm._e()],1):_vm._e()}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"side-drawer-container\",class:{ 'side-drawer-container-closed': _vm.closed, 'side-drawer-container-open': !_vm.closed }},[_c('div',{staticClass:\"side-drawer-darken\",class:{ 'side-drawer-darken-closed': _vm.closed}}),_vm._v(\" \"),_c('div',{staticClass:\"side-drawer\",class:{'side-drawer-closed': _vm.closed},on:{\"touchstart\":_vm.touchStart,\"touchmove\":_vm.touchMove}},[_c('div',{staticClass:\"side-drawer-heading\",on:{\"click\":_vm.toggleDrawer}},[(_vm.currentUser)?_c('UserCard',{attrs:{\"user\":_vm.currentUser,\"hide-bio\":true}}):_c('div',{staticClass:\"side-drawer-logo-wrapper\"},[_c('img',{attrs:{\"src\":_vm.logo}}),_vm._v(\" \"),_c('span',[_vm._v(_vm._s(_vm.sitename))])])],1),_vm._v(\" \"),_c('ul',[(!_vm.currentUser)?_c('li',{on:{\"click\":_vm.toggleDrawer}},[_c('router-link',{attrs:{\"to\":{ name: 'login' }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"login.login\"))+\"\\n \")])],1):_vm._e(),_vm._v(\" \"),(_vm.currentUser)?_c('li',{on:{\"click\":_vm.toggleDrawer}},[_c('router-link',{attrs:{\"to\":{ name: 'dms', params: { username: _vm.currentUser.screen_name } }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"nav.dms\"))+\"\\n \")])],1):_vm._e(),_vm._v(\" \"),(_vm.currentUser)?_c('li',{on:{\"click\":_vm.toggleDrawer}},[_c('router-link',{attrs:{\"to\":{ name: 'interactions', params: { username: _vm.currentUser.screen_name } }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"nav.interactions\"))+\"\\n \")])],1):_vm._e()]),_vm._v(\" \"),_c('ul',[(_vm.currentUser)?_c('li',{on:{\"click\":_vm.toggleDrawer}},[_c('router-link',{attrs:{\"to\":{ name: 'friends' }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"nav.timeline\"))+\"\\n \")])],1):_vm._e(),_vm._v(\" \"),(_vm.currentUser && _vm.currentUser.locked)?_c('li',{on:{\"click\":_vm.toggleDrawer}},[_c('router-link',{attrs:{\"to\":\"/friend-requests\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"nav.friend_requests\"))+\"\\n \"),(_vm.followRequestCount > 0)?_c('span',{staticClass:\"badge follow-request-count\"},[_vm._v(\"\\n \"+_vm._s(_vm.followRequestCount)+\"\\n \")]):_vm._e()])],1):_vm._e(),_vm._v(\" \"),_c('li',{on:{\"click\":_vm.toggleDrawer}},[_c('router-link',{attrs:{\"to\":\"/main/public\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"nav.public_tl\"))+\"\\n \")])],1),_vm._v(\" \"),_c('li',{on:{\"click\":_vm.toggleDrawer}},[_c('router-link',{attrs:{\"to\":\"/main/all\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"nav.twkn\"))+\"\\n \")])],1),_vm._v(\" \"),(_vm.currentUser && _vm.chat)?_c('li',{on:{\"click\":_vm.toggleDrawer}},[_c('router-link',{attrs:{\"to\":{ name: 'chat' }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"nav.chat\"))+\"\\n \")])],1):_vm._e()]),_vm._v(\" \"),_c('ul',[_c('li',{on:{\"click\":_vm.toggleDrawer}},[_c('router-link',{attrs:{\"to\":{ name: 'search' }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"nav.search\"))+\"\\n \")])],1),_vm._v(\" \"),(_vm.currentUser && _vm.suggestionsEnabled)?_c('li',{on:{\"click\":_vm.toggleDrawer}},[_c('router-link',{attrs:{\"to\":{ name: 'who-to-follow' }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"nav.who_to_follow\"))+\"\\n \")])],1):_vm._e(),_vm._v(\" \"),_c('li',{on:{\"click\":_vm.toggleDrawer}},[_c('router-link',{attrs:{\"to\":{ name: 'settings' }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"settings.settings\"))+\"\\n \")])],1),_vm._v(\" \"),_c('li',{on:{\"click\":_vm.toggleDrawer}},[_c('router-link',{attrs:{\"to\":{ name: 'about'}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"nav.about\"))+\"\\n \")])],1),_vm._v(\" \"),(_vm.currentUser && _vm.currentUser.role === 'admin')?_c('li',{on:{\"click\":_vm.toggleDrawer}},[_c('a',{attrs:{\"href\":\"/pleroma/admin/#/login-pleroma\",\"target\":\"_blank\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"nav.administration\"))+\"\\n \")])]):_vm._e(),_vm._v(\" \"),(_vm.currentUser)?_c('li',{on:{\"click\":_vm.toggleDrawer}},[_c('a',{attrs:{\"href\":\"#\"},on:{\"click\":_vm.doLogout}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"login.logout\"))+\"\\n \")])]):_vm._e()])]),_vm._v(\" \"),_c('div',{staticClass:\"side-drawer-click-outside\",class:{'side-drawer-click-outside-closed': _vm.closed},on:{\"click\":function($event){$event.stopPropagation();$event.preventDefault();return _vm.toggleDrawer($event)}}})])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return (_vm.isLoggedIn)?_c('div',[_c('button',{staticClass:\"new-status-button\",class:{ 'hidden': _vm.isHidden },on:{\"click\":_vm.openPostForm}},[_c('i',{staticClass:\"icon-edit\"})])]):_vm._e()}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('nav',{staticClass:\"nav-bar container\",attrs:{\"id\":\"nav\"}},[_c('div',{staticClass:\"mobile-inner-nav\",on:{\"click\":function($event){_vm.scrollToTop()}}},[_c('div',{staticClass:\"item\"},[_c('a',{staticClass:\"mobile-nav-button\",attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.stopPropagation();$event.preventDefault();_vm.toggleMobileSidebar()}}},[_c('i',{staticClass:\"button-icon icon-menu\"})]),_vm._v(\" \"),_c('router-link',{staticClass:\"site-name\",attrs:{\"to\":{ name: 'root' },\"active-class\":\"home\"}},[_vm._v(\"\\n \"+_vm._s(_vm.sitename)+\"\\n \")])],1),_vm._v(\" \"),_c('div',{staticClass:\"item right\"},[(_vm.currentUser)?_c('a',{staticClass:\"mobile-nav-button\",attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.stopPropagation();$event.preventDefault();_vm.openMobileNotifications()}}},[_c('i',{staticClass:\"button-icon icon-bell-alt\"}),_vm._v(\" \"),(_vm.unseenNotificationsCount)?_c('div',{staticClass:\"alert-dot\"}):_vm._e()]):_vm._e()])])]),_vm._v(\" \"),(_vm.currentUser)?_c('div',{staticClass:\"mobile-notifications-drawer\",class:{ 'closed': !_vm.notificationsOpen },on:{\"touchstart\":function($event){$event.stopPropagation();return _vm.notificationsTouchStart($event)},\"touchmove\":function($event){$event.stopPropagation();return _vm.notificationsTouchMove($event)}}},[_c('div',{staticClass:\"mobile-notifications-header\"},[_c('span',{staticClass:\"title\"},[_vm._v(_vm._s(_vm.$t('notifications.notifications')))]),_vm._v(\" \"),_c('a',{staticClass:\"mobile-nav-button\",on:{\"click\":function($event){$event.stopPropagation();$event.preventDefault();_vm.closeMobileNotifications()}}},[_c('i',{staticClass:\"button-icon icon-cancel\"})])]),_vm._v(\" \"),_c('div',{staticClass:\"mobile-notifications\",on:{\"scroll\":_vm.onScroll}},[_c('Notifications',{ref:\"notifications\",attrs:{\"no-heading\":true}})],1)]):_vm._e(),_vm._v(\" \"),_c('SideDrawer',{ref:\"sideDrawer\",attrs:{\"logout\":_vm.logout}})],1)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return (_vm.isOpen)?_c('div',{staticClass:\"modal-view\",on:{\"click\":_vm.closeModal}},[_c('div',{staticClass:\"user-reporting-panel panel\",on:{\"click\":function($event){$event.stopPropagation();}}},[_c('div',{staticClass:\"panel-heading\"},[_c('div',{staticClass:\"title\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_reporting.title', [_vm.user.screen_name]))+\"\\n \")])]),_vm._v(\" \"),_c('div',{staticClass:\"panel-body\"},[_c('div',{staticClass:\"user-reporting-panel-left\"},[_c('div',[_c('p',[_vm._v(_vm._s(_vm.$t('user_reporting.add_comment_description')))]),_vm._v(\" \"),_c('textarea',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.comment),expression:\"comment\"}],staticClass:\"form-control\",attrs:{\"placeholder\":_vm.$t('user_reporting.additional_comments'),\"rows\":\"1\"},domProps:{\"value\":(_vm.comment)},on:{\"input\":[function($event){if($event.target.composing){ return; }_vm.comment=$event.target.value},_vm.resize]}})]),_vm._v(\" \"),(!_vm.user.is_local)?_c('div',[_c('p',[_vm._v(_vm._s(_vm.$t('user_reporting.forward_description')))]),_vm._v(\" \"),_c('Checkbox',{model:{value:(_vm.forward),callback:function ($$v) {_vm.forward=$$v},expression:\"forward\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_reporting.forward_to', [_vm.remoteInstance]))+\"\\n \")])],1):_vm._e(),_vm._v(\" \"),_c('div',[_c('button',{staticClass:\"btn btn-default\",attrs:{\"disabled\":_vm.processing},on:{\"click\":_vm.reportUser}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_reporting.submit'))+\"\\n \")]),_vm._v(\" \"),(_vm.error)?_c('div',{staticClass:\"alert error\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_reporting.generic_error'))+\"\\n \")]):_vm._e()])]),_vm._v(\" \"),_c('div',{staticClass:\"user-reporting-panel-right\"},[_c('List',{attrs:{\"items\":_vm.statuses},scopedSlots:_vm._u([{key:\"item\",fn:function(ref){\nvar item = ref.item;\nreturn [_c('div',{staticClass:\"status-fadein user-reporting-panel-sitem\"},[_c('Status',{attrs:{\"in-conversation\":false,\"focused\":false,\"statusoid\":item}}),_vm._v(\" \"),_c('Checkbox',{attrs:{\"checked\":_vm.isChecked(item.id)},on:{\"change\":function (checked) { return _vm.toggleStatus(checked, item.id); }}})],1)]}}])})],1)])])]):_vm._e()}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return (_vm.isLoggedIn && !_vm.resettingForm)?_c('div',{directives:[{name:\"show\",rawName:\"v-show\",value:(_vm.modalActivated),expression:\"modalActivated\"}],staticClass:\"post-form-modal-view modal-view\",on:{\"click\":_vm.closeModal}},[_c('div',{staticClass:\"post-form-modal-panel panel\",on:{\"click\":function($event){$event.stopPropagation();}}},[_c('div',{staticClass:\"panel-heading\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('post_status.new_status'))+\"\\n \")]),_vm._v(\" \"),_c('PostStatusForm',_vm._b({staticClass:\"panel-body\",on:{\"posted\":_vm.closeModal}},'PostStatusForm',_vm.params,false))],1)]):_vm._e()}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{style:(_vm.bgAppStyle),attrs:{\"id\":\"app\"}},[_c('div',{staticClass:\"app-bg-wrapper\",style:(_vm.bgStyle),attrs:{\"id\":\"app_bg_wrapper\"}}),_vm._v(\" \"),(_vm.isMobileLayout)?_c('MobileNav'):_c('nav',{staticClass:\"nav-bar container\",attrs:{\"id\":\"nav\"},on:{\"click\":function($event){_vm.scrollToTop()}}},[_c('div',{staticClass:\"inner-nav\"},[_c('div',{staticClass:\"logo\",style:(_vm.logoBgStyle)},[_c('div',{staticClass:\"mask\",style:(_vm.logoMaskStyle)}),_vm._v(\" \"),_c('img',{style:(_vm.logoStyle),attrs:{\"src\":_vm.logo}})]),_vm._v(\" \"),_c('div',{staticClass:\"item\"},[_c('router-link',{staticClass:\"site-name\",attrs:{\"to\":{ name: 'root' },\"active-class\":\"home\"}},[_vm._v(\"\\n \"+_vm._s(_vm.sitename)+\"\\n \")])],1),_vm._v(\" \"),_c('div',{staticClass:\"item right\"},[_c('search-bar',{staticClass:\"nav-icon mobile-hidden\",on:{\"toggled\":_vm.onSearchBarToggled},nativeOn:{\"click\":function($event){$event.stopPropagation();}}}),_vm._v(\" \"),_c('router-link',{staticClass:\"mobile-hidden\",attrs:{\"to\":{ name: 'settings'}}},[_c('i',{staticClass:\"button-icon icon-cog nav-icon\",attrs:{\"title\":_vm.$t('nav.preferences')}})]),_vm._v(\" \"),(_vm.currentUser && _vm.currentUser.role === 'admin')?_c('a',{staticClass:\"mobile-hidden\",attrs:{\"href\":\"/pleroma/admin/#/login-pleroma\",\"target\":\"_blank\"}},[_c('i',{staticClass:\"button-icon icon-gauge nav-icon\",attrs:{\"title\":_vm.$t('nav.administration')}})]):_vm._e(),_vm._v(\" \"),(_vm.currentUser)?_c('a',{staticClass:\"mobile-hidden\",attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();return _vm.logout($event)}}},[_c('i',{staticClass:\"button-icon icon-logout nav-icon\",attrs:{\"title\":_vm.$t('login.logout')}})]):_vm._e()],1)])]),_vm._v(\" \"),_c('div',{staticClass:\"container\",attrs:{\"id\":\"content\"}},[_c('div',{staticClass:\"sidebar-flexer mobile-hidden\"},[_c('div',{staticClass:\"sidebar-bounds\"},[_c('div',{staticClass:\"sidebar-scroller\"},[_c('div',{staticClass:\"sidebar\"},[_c('user-panel'),_vm._v(\" \"),(!_vm.isMobileLayout)?_c('div',[_c('nav-panel'),_vm._v(\" \"),(_vm.showInstanceSpecificPanel)?_c('instance-specific-panel'):_vm._e(),_vm._v(\" \"),(!_vm.currentUser && _vm.showFeaturesPanel)?_c('features-panel'):_vm._e(),_vm._v(\" \"),(_vm.currentUser && _vm.suggestionsEnabled)?_c('who-to-follow-panel'):_vm._e(),_vm._v(\" \"),(_vm.currentUser)?_c('notifications'):_vm._e()],1):_vm._e()],1)])])]),_vm._v(\" \"),_c('div',{staticClass:\"main\"},[(!_vm.currentUser)?_c('div',{staticClass:\"login-hint panel panel-default\"},[_c('router-link',{staticClass:\"panel-body\",attrs:{\"to\":{ name: 'login' }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"login.hint\"))+\"\\n \")])],1):_vm._e(),_vm._v(\" \"),_c('transition',{attrs:{\"name\":\"fade\"}},[_c('router-view')],1)],1),_vm._v(\" \"),_c('media-modal')],1),_vm._v(\" \"),(_vm.currentUser && _vm.chat)?_c('chat-panel',{staticClass:\"floating-chat mobile-hidden\",attrs:{\"floating\":true}}):_vm._e(),_vm._v(\" \"),_c('MobilePostStatusButton'),_vm._v(\" \"),_c('UserReportingModal'),_vm._v(\" \"),_c('PostStatusModal'),_vm._v(\" \"),_c('portal-target',{attrs:{\"name\":\"modal\"}})],1)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","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","import { camelCase } from 'lodash'\n\nimport apiService from '../api/api.service.js'\n\nconst update = ({ store, statuses, timeline, showImmediately, userId }) => {\n const ccTimeline = camelCase(timeline)\n\n store.dispatch('setError', { value: false })\n\n store.dispatch('addNewStatuses', {\n timeline: ccTimeline,\n userId,\n statuses,\n showImmediately\n })\n}\n\nconst fetchAndUpdate = ({ store, credentials, timeline = 'friends', older = false, showImmediately = false, userId = false, tag = false, until }) => {\n const args = { timeline, credentials }\n const rootState = store.rootState || store.state\n const timelineData = rootState.statuses.timelines[camelCase(timeline)]\n const hideMutedPosts = typeof rootState.config.hideMutedPosts === 'undefined'\n ? rootState.instance.hideMutedPosts\n : rootState.config.hideMutedPosts\n\n if (older) {\n args['until'] = until || timelineData.minId\n } else {\n args['since'] = timelineData.maxId\n }\n\n args['userId'] = userId\n args['tag'] = tag\n args['withMuted'] = !hideMutedPosts\n\n const numStatusesBeforeFetch = timelineData.statuses.length\n\n return apiService.fetchTimeline(args)\n .then((statuses) => {\n if (!older && statuses.length >= 20 && !timelineData.loading && numStatusesBeforeFetch > 0) {\n store.dispatch('queueFlush', { timeline: timeline, id: timelineData.maxId })\n }\n update({ store, statuses, timeline, showImmediately, userId })\n return statuses\n }, () => store.dispatch('setError', { value: true }))\n}\n\nconst startFetching = ({ timeline = 'friends', credentials, store, userId = false, tag = false }) => {\n const rootState = store.rootState || store.state\n const timelineData = rootState.statuses.timelines[camelCase(timeline)]\n const showImmediately = timelineData.visibleStatuses.length === 0\n timelineData.userId = userId\n fetchAndUpdate({ timeline, credentials, store, showImmediately, userId, tag })\n const boundFetchAndUpdate = () => fetchAndUpdate({ timeline, credentials, store, userId, tag })\n return setInterval(boundFetchAndUpdate, 10000)\n}\nconst timelineFetcher = {\n fetchAndUpdate,\n startFetching\n}\n\nexport default timelineFetcher\n","import apiService from '../api/api.service.js'\n\nconst update = ({ store, notifications, older }) => {\n store.dispatch('setNotificationsError', { value: false })\n\n store.dispatch('addNewNotifications', { notifications, older })\n}\n\nconst fetchAndUpdate = ({ store, credentials, older = false }) => {\n const args = { credentials }\n const rootState = store.rootState || store.state\n const timelineData = rootState.statuses.notifications\n const hideMutedPosts = typeof rootState.config.hideMutedPosts === 'undefined'\n ? rootState.instance.hideMutedPosts\n : rootState.config.hideMutedPosts\n\n args['withMuted'] = !hideMutedPosts\n\n args['timeline'] = 'notifications'\n if (older) {\n if (timelineData.minId !== Number.POSITIVE_INFINITY) {\n args['until'] = timelineData.minId\n }\n return fetchNotifications({ store, args, older })\n } else {\n // fetch new notifications\n if (timelineData.maxId !== Number.POSITIVE_INFINITY) {\n args['since'] = timelineData.maxId\n }\n const result = fetchNotifications({ store, args, older })\n\n // load unread notifications repeatedly to provide consistency between browser tabs\n const notifications = timelineData.data\n const unread = notifications.filter(n => !n.seen).map(n => n.id)\n if (unread.length) {\n args['since'] = Math.min(...unread)\n fetchNotifications({ store, args, older })\n }\n\n return result\n }\n}\n\nconst fetchNotifications = ({ store, args, older }) => {\n return apiService.fetchTimeline(args)\n .then((notifications) => {\n update({ store, notifications, older })\n return notifications\n }, () => store.dispatch('setNotificationsError', { value: true }))\n .catch(() => store.dispatch('setNotificationsError', { value: true }))\n}\n\nconst startFetching = ({ credentials, store }) => {\n fetchAndUpdate({ credentials, store })\n const boundFetchAndUpdate = () => fetchAndUpdate({ credentials, store })\n // Initially there's set flag to silence all desktop notifications so\n // that there won't spam of them when user just opened up the FE we\n // reset that flag after a while to show new notifications once again.\n setTimeout(() => store.dispatch('setNotificationsSilence', false), 10000)\n return setInterval(boundFetchAndUpdate, 10000)\n}\n\nconst notificationsFetcher = {\n fetchAndUpdate,\n startFetching\n}\n\nexport default notificationsFetcher\n","// When contributing, please sort JSON before committing so it would be easier to see what's missing and what's being added compared to English and other languages. It's not obligatory, but just an advice.\n// To sort json use jq https://stedolan.github.io/jq and invoke it like `jq -S . xx.json > xx.sorted.json`, AFAIK, there's no inplace edit option like in sed\n// Also, when adding a new language to \"messages\" variable, please do it alphabetically by language code so that users can search or check their custom language easily.\n\n// For anyone contributing to old huge messages.js and in need to quickly convert it to JSON\n// sed command for converting currently formatted JS to JSON:\n// sed -i -e \"s/'//gm\" -e 's/\"/\\\\\"/gm' -re 's/^( +)(.+?): ((.+?))?(,?)(\\{?)$/\\1\"\\2\": \"\\4\"/gm' -e 's/\\\"\\{\\\"/{/g' -e 's/,\"$/\",/g' file.json\n// There's only problem that apostrophe character ' gets replaced by \\\\ so you have to fix it manually, sorry.\n\nconst messages = {\n ar: require('./ar.json'),\n ca: require('./ca.json'),\n cs: require('./cs.json'),\n de: require('./de.json'),\n en: require('./en.json'),\n eo: require('./eo.json'),\n es: require('./es.json'),\n et: require('./et.json'),\n eu: require('./eu.json'),\n fi: require('./fi.json'),\n fr: require('./fr.json'),\n ga: require('./ga.json'),\n he: require('./he.json'),\n hu: require('./hu.json'),\n it: require('./it.json'),\n ja: require('./ja.json'),\n ja_pedantic: require('./ja_pedantic.json'),\n ko: require('./ko.json'),\n nb: require('./nb.json'),\n nl: require('./nl.json'),\n oc: require('./oc.json'),\n pl: require('./pl.json'),\n pt: require('./pt.json'),\n ro: require('./ro.json'),\n ru: require('./ru.json'),\n te: require('./te.json'),\n zh: require('./zh.json')\n}\n\nexport default messages\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-2a0da20a\\\",\\\"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","/* script */\nexport * from \"!!babel-loader!./video_attachment.js\"\nimport __vue_script__ from \"!!babel-loader!./video_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-6fce6a82\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./video_attachment.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","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","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","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!./scope_selector.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./scope_selector.js\"\nimport __vue_script__ from \"!!babel-loader!./scope_selector.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-28e8cbf1\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./scope_selector.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","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_input.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./emoji_input.js\"\nimport __vue_script__ from \"!!babel-loader!./emoji_input.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-55e3ae24\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./emoji_input.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","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 { 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().startsWith(noPrefix))\n .sort((a, b) => {\n let aScore = 0\n let bScore = 0\n\n // Make custom emojis a priority\n aScore += a.imageUrl ? 10 : 0\n bScore += b.imageUrl ? 10 : 0\n\n // Sort alphabetically\n const alphabetically = a.displayText > b.displayText ? 1 : -1\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","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!./remote_follow.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./remote_follow.js\"\nimport __vue_script__ from \"!!babel-loader!./remote_follow.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-e95e446e\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./remote_follow.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","/* 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","const fetchUser = (attempt, user, store) => new Promise((resolve, reject) => {\n setTimeout(() => {\n store.state.api.backendInteractor.fetchUser({ id: user.id })\n .then((user) => store.commit('addNewUsers', [user]))\n .then(() => resolve([user.following, user.requested, user.locked, attempt]))\n .catch((e) => reject(e))\n }, 500)\n}).then(([following, sent, locked, attempt]) => {\n if (!following && !(locked && sent) && attempt <= 3) {\n // If we BE reports that we still not following that user - retry,\n // increment attempts by one\n fetchUser(++attempt, user, store)\n }\n})\n\nexport const requestFollow = (user, store) => new Promise((resolve, reject) => {\n store.state.api.backendInteractor.followUser(user.id)\n .then((updated) => {\n store.commit('updateUserRelationship', [updated])\n\n if (updated.following || (user.locked && user.requested)) {\n // If we get result immediately or the account is locked, just stop.\n resolve()\n return\n }\n\n // But usually we don't get result immediately, so we ask server\n // for updated user profile to confirm if we are following them\n // Sometimes it takes several tries. Sometimes we end up not following\n // user anyway, probably because they locked themselves and we\n // don't know that yet.\n // Recursive Promise, it will call itself up to 3 times.\n\n return fetchUser(1, user, store)\n .then(() => {\n resolve()\n })\n })\n})\n\nexport const requestUnfollow = (user, store) => new Promise((resolve, reject) => {\n store.state.api.backendInteractor.unfollowUser(user.id)\n .then((updated) => {\n store.commit('updateUserRelationship', [updated])\n resolve({\n updated\n })\n })\n})\n","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 isFunction from 'lodash/isFunction'\n\nconst getComponentOptions = (Component) => (isFunction(Component)) ? Component.options : Component\n\nconst getComponentProps = (Component) => getComponentOptions(Component).props\n\nexport {\n getComponentOptions,\n getComponentProps\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!./style_switcher.scss\")\n}\n/* script */\nexport * from \"!!babel-loader!./style_switcher.js\"\nimport __vue_script__ from \"!!babel-loader!./style_switcher.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-ea88f12a\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./style_switcher.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","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!./color_input.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!../../../node_modules/vue-loader/lib/selector?type=script&index=0!./color_input.vue\"\nimport __vue_script__ from \"!!babel-loader!../../../node_modules/vue-loader/lib/selector?type=script&index=0!./color_input.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-a377bb38\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./color_input.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","/* script */\nexport * from \"!!babel-loader!../../../node_modules/vue-loader/lib/selector?type=script&index=0!./opacity_input.vue\"\nimport __vue_script__ from \"!!babel-loader!../../../node_modules/vue-loader/lib/selector?type=script&index=0!./opacity_input.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-87056ae2\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./opacity_input.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","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-02484cf4\\\",\\\"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","/* script */\nexport * from \"!!babel-loader!./confirm.js\"\nimport __vue_script__ from \"!!babel-loader!./confirm.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-20b6e7b3\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./confirm.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","import LoginForm from '../login_form/login_form.vue'\nimport MFARecoveryForm from '../mfa_form/recovery_form.vue'\nimport MFATOTPForm from '../mfa_form/totp_form.vue'\nimport { mapGetters } from 'vuex'\n\nconst AuthForm = {\n name: 'AuthForm',\n render (createElement) {\n return createElement('component', { is: this.authForm })\n },\n computed: {\n authForm () {\n if (this.requiredTOTP) { return 'MFATOTPForm' }\n if (this.requiredRecovery) { return 'MFARecoveryForm' }\n return 'LoginForm'\n },\n ...mapGetters('authFlow', ['requiredTOTP', 'requiredRecovery'])\n },\n components: {\n MFARecoveryForm,\n MFATOTPForm,\n LoginForm\n }\n}\n\nexport default AuthForm\n","const verifyOTPCode = ({ app, instance, mfaToken, code }) => {\n const url = `${instance}/oauth/mfa/challenge`\n const form = new window.FormData()\n\n form.append('client_id', app.client_id)\n form.append('client_secret', app.client_secret)\n form.append('mfa_token', mfaToken)\n form.append('code', code)\n form.append('challenge_type', 'totp')\n\n return window.fetch(url, {\n method: 'POST',\n body: form\n }).then((data) => data.json())\n}\n\nconst verifyRecoveryCode = ({ app, instance, mfaToken, code }) => {\n const url = `${instance}/oauth/mfa/challenge`\n const form = new window.FormData()\n\n form.append('client_id', app.client_id)\n form.append('client_secret', app.client_secret)\n form.append('mfa_token', mfaToken)\n form.append('code', code)\n form.append('challenge_type', 'recovery')\n\n return window.fetch(url, {\n method: 'POST',\n body: form\n }).then((data) => data.json())\n}\n\nconst mfa = {\n verifyOTPCode,\n verifyRecoveryCode\n}\n\nexport default mfa\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!./chat_panel.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./chat_panel.js\"\nimport __vue_script__ from \"!!babel-loader!./chat_panel.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-7ea51572\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./chat_panel.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","/* script */\nexport * from \"!!babel-loader!./instance_specific_panel.js\"\nimport __vue_script__ from \"!!babel-loader!./instance_specific_panel.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-5b01187b\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./instance_specific_panel.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","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!./features_panel.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./features_panel.js\"\nimport __vue_script__ from \"!!babel-loader!./features_panel.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-3443e05c\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./features_panel.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","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!./side_drawer.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./side_drawer.js\"\nimport __vue_script__ from \"!!babel-loader!./side_drawer.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-5c94965a\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./side_drawer.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","\nconst DIRECTION_LEFT = [-1, 0]\nconst DIRECTION_RIGHT = [1, 0]\nconst DIRECTION_UP = [0, -1]\nconst DIRECTION_DOWN = [0, 1]\n\nconst deltaCoord = (oldCoord, newCoord) => [newCoord[0] - oldCoord[0], newCoord[1] - oldCoord[1]]\n\nconst touchEventCoord = e => ([e.touches[0].screenX, e.touches[0].screenY])\n\nconst vectorLength = v => Math.sqrt(v[0] * v[0] + v[1] * v[1])\n\nconst perpendicular = v => [v[1], -v[0]]\n\nconst dotProduct = (v1, v2) => v1[0] * v2[0] + v1[1] * v2[1]\n\nconst project = (v1, v2) => {\n const scalar = (dotProduct(v1, v2) / dotProduct(v2, v2))\n return [scalar * v2[0], scalar * v2[1]]\n}\n\n// direction: either use the constants above or an arbitrary 2d vector.\n// threshold: how many Px to move from touch origin before checking if the\n// callback should be called.\n// divergentTolerance: a scalar for much of divergent direction we tolerate when\n// above threshold. for example, with 1.0 we only call the callback if\n// divergent component of delta is < 1.0 * direction component of delta.\nconst swipeGesture = (direction, onSwipe, threshold = 30, perpendicularTolerance = 1.0) => {\n return {\n direction,\n onSwipe,\n threshold,\n perpendicularTolerance,\n _startPos: [0, 0],\n _swiping: false\n }\n}\n\nconst beginSwipe = (event, gesture) => {\n gesture._startPos = touchEventCoord(event)\n gesture._swiping = true\n}\n\nconst updateSwipe = (event, gesture) => {\n if (!gesture._swiping) return\n // movement too small\n const delta = deltaCoord(gesture._startPos, touchEventCoord(event))\n if (vectorLength(delta) < gesture.threshold) return\n // movement is opposite from direction\n if (dotProduct(delta, gesture.direction) < 0) return\n // movement perpendicular to direction is too much\n const towardsDir = project(delta, gesture.direction)\n const perpendicularDir = perpendicular(gesture.direction)\n const towardsPerpendicular = project(delta, perpendicularDir)\n if (\n vectorLength(towardsDir) * gesture.perpendicularTolerance <\n vectorLength(towardsPerpendicular)\n ) return\n\n gesture.onSwipe()\n gesture._swiping = false\n}\n\nconst GestureService = {\n DIRECTION_LEFT,\n DIRECTION_RIGHT,\n DIRECTION_UP,\n DIRECTION_DOWN,\n swipeGesture,\n beginSwipe,\n updateSwipe\n}\n\nexport default GestureService\n","\nexport const windowWidth = () =>\n window.innerWidth ||\n document.documentElement.clientWidth ||\n document.body.clientWidth\n","import Vue from 'vue'\nimport VueRouter from 'vue-router'\nimport Vuex from 'vuex'\n\nimport interfaceModule from './modules/interface.js'\nimport instanceModule from './modules/instance.js'\nimport statusesModule from './modules/statuses.js'\nimport usersModule from './modules/users.js'\nimport apiModule from './modules/api.js'\nimport configModule from './modules/config.js'\nimport chatModule from './modules/chat.js'\nimport oauthModule from './modules/oauth.js'\nimport authFlowModule from './modules/auth_flow.js'\nimport mediaViewerModule from './modules/media_viewer.js'\nimport oauthTokensModule from './modules/oauth_tokens.js'\nimport reportsModule from './modules/reports.js'\nimport pollsModule from './modules/polls.js'\nimport postStatusModule from './modules/postStatus.js'\n\nimport VueI18n from 'vue-i18n'\n\nimport createPersistedState from './lib/persisted_state.js'\nimport pushNotifications from './lib/push_notifications_plugin.js'\n\nimport messages from './i18n/messages.js'\n\nimport VueChatScroll from 'vue-chat-scroll'\nimport VueClickOutside from 'v-click-outside'\nimport PortalVue from 'portal-vue'\nimport VBodyScrollLock from './directives/body_scroll_lock'\nimport VTooltip from 'v-tooltip'\n\nimport afterStoreSetup from './boot/after_store.js'\n\nconst currentLocale = (window.navigator.language || 'en').split('-')[0]\n\nVue.use(Vuex)\nVue.use(VueRouter)\nVue.use(VueI18n)\nVue.use(VueChatScroll)\nVue.use(VueClickOutside)\nVue.use(PortalVue)\nVue.use(VBodyScrollLock)\nVue.use(VTooltip)\n\nconst i18n = new VueI18n({\n // By default, use the browser locale, we will update it if neccessary\n locale: currentLocale,\n fallbackLocale: 'en',\n messages\n})\n\nconst persistedStateOptions = {\n paths: [\n 'config',\n 'users.lastLoginName',\n 'oauth'\n ]\n};\n\n(async () => {\n const persistedState = await createPersistedState(persistedStateOptions)\n const store = new Vuex.Store({\n modules: {\n i18n: {\n getters: {\n i18n: () => i18n\n }\n },\n interface: interfaceModule,\n instance: instanceModule,\n statuses: statusesModule,\n users: usersModule,\n api: apiModule,\n config: configModule,\n chat: chatModule,\n oauth: oauthModule,\n authFlow: authFlowModule,\n mediaViewer: mediaViewerModule,\n oauthTokens: oauthTokensModule,\n reports: reportsModule,\n polls: pollsModule,\n postStatus: postStatusModule\n },\n plugins: [persistedState, pushNotifications],\n strict: false // Socket modifies itself, let's ignore this for now.\n // strict: process.env.NODE_ENV !== 'production'\n })\n\n afterStoreSetup({ store, i18n })\n})()\n\n// These are inlined by webpack's DefinePlugin\n/* eslint-disable */\nwindow.___pleromafe_mode = process.env\nwindow.___pleromafe_commit_hash = COMMIT_HASH\nwindow.___pleromafe_dev_overrides = DEV_OVERRIDES\n","import { set, delete as del } from 'vue'\n\nconst defaultState = {\n settings: {\n currentSaveStateNotice: null,\n noticeClearTimeout: null,\n notificationPermission: null\n },\n browserSupport: {\n cssFilter: window.CSS && window.CSS.supports && (\n window.CSS.supports('filter', 'drop-shadow(0 0)') ||\n window.CSS.supports('-webkit-filter', 'drop-shadow(0 0)')\n )\n },\n mobileLayout: false\n}\n\nconst interfaceMod = {\n state: defaultState,\n mutations: {\n settingsSaved (state, { success, error }) {\n if (success) {\n if (state.noticeClearTimeout) {\n clearTimeout(state.noticeClearTimeout)\n }\n set(state.settings, 'currentSaveStateNotice', { error: false, data: success })\n set(state.settings, 'noticeClearTimeout',\n setTimeout(() => del(state.settings, 'currentSaveStateNotice'), 2000))\n } else {\n set(state.settings, 'currentSaveStateNotice', { error: true, errorData: error })\n }\n },\n setNotificationPermission (state, permission) {\n state.notificationPermission = permission\n },\n setMobileLayout (state, value) {\n state.mobileLayout = value\n }\n },\n actions: {\n setPageTitle ({ rootState }, option = '') {\n document.title = `${option} ${rootState.instance.name}`\n },\n settingsSaved ({ commit, dispatch }, { success, error }) {\n commit('settingsSaved', { success, error })\n },\n setNotificationPermission ({ commit }, permission) {\n commit('setNotificationPermission', permission)\n },\n setMobileLayout ({ commit }, value) {\n commit('setMobileLayout', value)\n }\n }\n}\n\nexport default interfaceMod\n","import { set } from 'vue'\nimport { setPreset } from '../services/style_setter/style_setter.js'\n\nconst defaultState = {\n // Stuff from static/config.json and apiConfig\n name: 'Pleroma FE',\n registrationOpen: true,\n safeDM: true,\n textlimit: 5000,\n server: 'http://localhost:4040/',\n theme: 'pleroma-dark',\n background: '/static/aurora_borealis.jpg',\n logo: '/static/logo.png',\n logoMask: true,\n logoMargin: '.2em',\n redirectRootNoLogin: '/main/all',\n redirectRootLogin: '/main/friends',\n showInstanceSpecificPanel: false,\n alwaysShowSubjectInput: true,\n hideMutedPosts: false,\n collapseMessageWithSubject: false,\n hidePostStats: false,\n hideUserStats: false,\n hideFilteredStatuses: false,\n disableChat: false,\n scopeCopy: true,\n subjectLineBehavior: 'email',\n postContentType: 'text/plain',\n nsfwCensorImage: undefined,\n vapidPublicKey: undefined,\n noAttachmentLinks: false,\n showFeaturesPanel: true,\n minimalScopesMode: false,\n\n // Nasty stuff\n pleromaBackend: true,\n emoji: [],\n customEmoji: [],\n restrictedNicknames: [],\n postFormats: [],\n\n // Feature-set, apparently, not everything here is reported...\n mediaProxyAvailable: false,\n chatAvailable: false,\n gopherAvailable: false,\n suggestionsEnabled: false,\n suggestionsWeb: '',\n\n // Html stuff\n instanceSpecificPanelContent: '',\n tos: '',\n\n // Version Information\n backendVersion: '',\n frontendVersion: '',\n\n pollsAvailable: false,\n pollLimits: {\n max_options: 4,\n max_option_chars: 255,\n min_expiration: 60,\n max_expiration: 60 * 60 * 24\n }\n}\n\nconst instance = {\n state: defaultState,\n mutations: {\n setInstanceOption (state, { name, value }) {\n if (typeof value !== 'undefined') {\n set(state, name, value)\n }\n }\n },\n actions: {\n setInstanceOption ({ commit, dispatch }, { name, value }) {\n commit('setInstanceOption', { name, value })\n switch (name) {\n case 'name':\n dispatch('setPageTitle')\n break\n case 'chatAvailable':\n if (value) {\n dispatch('initializeSocket')\n }\n break\n }\n },\n setTheme ({ commit }, themeName) {\n commit('setInstanceOption', { name: 'theme', value: themeName })\n return setPreset(themeName, commit)\n }\n }\n}\n\nexport default instance\n","import { remove, slice, each, findIndex, find, maxBy, minBy, merge, first, last, isArray, omitBy } from 'lodash'\nimport { set } from 'vue'\nimport apiService from '../services/api/api.service.js'\n// import parse from '../services/status_parser/status_parser.js'\n\nconst emptyTl = (userId = 0) => ({\n statuses: [],\n statusesObject: {},\n faves: [],\n visibleStatuses: [],\n visibleStatusesObject: {},\n newStatusCount: 0,\n maxId: 0,\n minId: 0,\n minVisibleId: 0,\n loading: false,\n followers: [],\n friends: [],\n userId,\n flushMarker: 0\n})\n\nconst emptyNotifications = () => ({\n desktopNotificationSilence: true,\n maxId: 0,\n minId: Number.POSITIVE_INFINITY,\n data: [],\n idStore: {},\n loading: false,\n error: false\n})\n\nexport const defaultState = () => ({\n allStatuses: [],\n allStatusesObject: {},\n conversationsObject: {},\n maxId: 0,\n notifications: emptyNotifications(),\n favorites: new Set(),\n error: false,\n timelines: {\n mentions: emptyTl(),\n public: emptyTl(),\n user: emptyTl(),\n favorites: emptyTl(),\n media: emptyTl(),\n publicAndExternal: emptyTl(),\n friends: emptyTl(),\n tag: emptyTl(),\n dms: emptyTl()\n }\n})\n\nexport const prepareStatus = (status) => {\n // Set deleted flag\n status.deleted = false\n\n // To make the array reactive\n status.attachments = status.attachments || []\n\n return status\n}\n\nconst visibleNotificationTypes = (rootState) => {\n return [\n rootState.config.notificationVisibility.likes && 'like',\n rootState.config.notificationVisibility.mentions && 'mention',\n rootState.config.notificationVisibility.repeats && 'repeat',\n rootState.config.notificationVisibility.follows && 'follow'\n ].filter(_ => _)\n}\n\nconst mergeOrAdd = (arr, obj, item) => {\n const oldItem = obj[item.id]\n\n if (oldItem) {\n // We already have this, so only merge the new info.\n // We ignore null values to avoid overwriting existing properties with missing data\n // we also skip 'user' because that is handled by users module\n merge(oldItem, omitBy(item, (v, k) => v === null || k === 'user'))\n // Reactivity fix.\n oldItem.attachments.splice(oldItem.attachments.length)\n return { item: oldItem, new: false }\n } else {\n // This is a new item, prepare it\n prepareStatus(item)\n arr.push(item)\n set(obj, item.id, item)\n return { item, new: true }\n }\n}\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\nconst sortTimeline = (timeline) => {\n timeline.visibleStatuses = timeline.visibleStatuses.sort(sortById)\n timeline.statuses = timeline.statuses.sort(sortById)\n timeline.minVisibleId = (last(timeline.visibleStatuses) || {}).id\n return timeline\n}\n\n// Add status to the global storages (arrays and objects maintaining statuses) except timelines\nconst addStatusToGlobalStorage = (state, data) => {\n const result = mergeOrAdd(state.allStatuses, state.allStatusesObject, data)\n if (result.new) {\n // Add to conversation\n const status = result.item\n const conversationsObject = state.conversationsObject\n const conversationId = status.statusnet_conversation_id\n if (conversationsObject[conversationId]) {\n conversationsObject[conversationId].push(status)\n } else {\n set(conversationsObject, conversationId, [status])\n }\n }\n return result\n}\n\n// Remove status from the global storages (arrays and objects maintaining statuses) except timelines\nconst removeStatusFromGlobalStorage = (state, status) => {\n remove(state.allStatuses, { id: status.id })\n\n // TODO: Need to remove from allStatusesObject?\n\n // Remove possible notification\n remove(state.notifications.data, ({ action: { id } }) => id === status.id)\n\n // Remove from conversation\n const conversationId = status.statusnet_conversation_id\n if (state.conversationsObject[conversationId]) {\n remove(state.conversationsObject[conversationId], { id: status.id })\n }\n}\n\nconst addNewStatuses = (state, { statuses, showImmediately = false, timeline, user = {},\n noIdUpdate = false, userId }) => {\n // Sanity check\n if (!isArray(statuses)) {\n return false\n }\n\n const allStatuses = state.allStatuses\n const timelineObject = state.timelines[timeline]\n\n const maxNew = statuses.length > 0 ? maxBy(statuses, 'id').id : 0\n const minNew = statuses.length > 0 ? minBy(statuses, 'id').id : 0\n const newer = timeline && (maxNew > timelineObject.maxId || timelineObject.maxId === 0) && statuses.length > 0\n const older = timeline && (minNew < timelineObject.minId || timelineObject.minId === 0) && statuses.length > 0\n\n if (!noIdUpdate && newer) {\n timelineObject.maxId = maxNew\n }\n if (!noIdUpdate && older) {\n timelineObject.minId = minNew\n }\n\n // This makes sure that user timeline won't get data meant for other\n // user. I.e. opening different user profiles makes request which could\n // return data late after user already viewing different user profile\n if ((timeline === 'user' || timeline === 'media') && timelineObject.userId !== userId) {\n return\n }\n\n const addStatus = (data, showImmediately, addToTimeline = true) => {\n const result = addStatusToGlobalStorage(state, data)\n const status = result.item\n\n if (result.new) {\n // We are mentioned in a post\n if (status.type === 'status' && find(status.attentions, { id: user.id })) {\n const mentions = state.timelines.mentions\n\n // Add the mention to the mentions timeline\n if (timelineObject !== mentions) {\n mergeOrAdd(mentions.statuses, mentions.statusesObject, status)\n mentions.newStatusCount += 1\n\n sortTimeline(mentions)\n }\n }\n if (status.visibility === 'direct') {\n const dms = state.timelines.dms\n\n mergeOrAdd(dms.statuses, dms.statusesObject, status)\n dms.newStatusCount += 1\n\n sortTimeline(dms)\n }\n }\n\n // Decide if we should treat the status as new for this timeline.\n let resultForCurrentTimeline\n // Some statuses should only be added to the global status repository.\n if (timeline && addToTimeline) {\n resultForCurrentTimeline = mergeOrAdd(timelineObject.statuses, timelineObject.statusesObject, status)\n }\n\n if (timeline && showImmediately) {\n // Add it directly to the visibleStatuses, don't change\n // newStatusCount\n mergeOrAdd(timelineObject.visibleStatuses, timelineObject.visibleStatusesObject, status)\n } else if (timeline && addToTimeline && resultForCurrentTimeline.new) {\n // Just change newStatuscount\n timelineObject.newStatusCount += 1\n }\n\n return status\n }\n\n const favoriteStatus = (favorite, counter) => {\n const status = find(allStatuses, { id: favorite.in_reply_to_status_id })\n if (status) {\n // This is our favorite, so the relevant bit.\n if (favorite.user.id === user.id) {\n status.favorited = true\n } else {\n status.fave_num += 1\n }\n }\n return status\n }\n\n const processors = {\n 'status': (status) => {\n addStatus(status, showImmediately)\n },\n 'retweet': (status) => {\n // RetweetedStatuses are never shown immediately\n const retweetedStatus = addStatus(status.retweeted_status, false, false)\n\n let retweet\n // If the retweeted status is already there, don't add the retweet\n // to the timeline.\n if (timeline && find(timelineObject.statuses, (s) => {\n if (s.retweeted_status) {\n return s.id === retweetedStatus.id || s.retweeted_status.id === retweetedStatus.id\n } else {\n return s.id === retweetedStatus.id\n }\n })) {\n // Already have it visible (either as the original or another RT), don't add to timeline, don't show.\n retweet = addStatus(status, false, false)\n } else {\n retweet = addStatus(status, showImmediately)\n }\n\n retweet.retweeted_status = retweetedStatus\n },\n 'favorite': (favorite) => {\n // Only update if this is a new favorite.\n // Ignore our own favorites because we get info about likes as response to like request\n if (!state.favorites.has(favorite.id)) {\n state.favorites.add(favorite.id)\n favoriteStatus(favorite)\n }\n },\n 'deletion': (deletion) => {\n const uri = deletion.uri\n const status = find(allStatuses, { uri })\n if (!status) {\n return\n }\n\n removeStatusFromGlobalStorage(state, status)\n\n if (timeline) {\n remove(timelineObject.statuses, { uri })\n remove(timelineObject.visibleStatuses, { uri })\n }\n },\n 'follow': (follow) => {\n // NOOP, it is known status but we don't do anything about it for now\n },\n 'default': (unknown) => {\n console.log('unknown status type')\n console.log(unknown)\n }\n }\n\n each(statuses, (status) => {\n const type = status.type\n const processor = processors[type] || processors['default']\n processor(status)\n })\n\n // Keep the visible statuses sorted\n if (timeline) {\n sortTimeline(timelineObject)\n }\n}\n\nconst addNewNotifications = (state, { dispatch, notifications, older, visibleNotificationTypes, rootGetters }) => {\n each(notifications, (notification) => {\n if (notification.type !== 'follow') {\n notification.action = addStatusToGlobalStorage(state, notification.action).item\n notification.status = notification.status && addStatusToGlobalStorage(state, notification.status).item\n }\n\n // Only add a new notification if we don't have one for the same action\n if (!state.notifications.idStore.hasOwnProperty(notification.id)) {\n state.notifications.maxId = notification.id > state.notifications.maxId\n ? notification.id\n : state.notifications.maxId\n state.notifications.minId = notification.id < state.notifications.minId\n ? notification.id\n : state.notifications.minId\n\n state.notifications.data.push(notification)\n state.notifications.idStore[notification.id] = notification\n\n if ('Notification' in window && window.Notification.permission === 'granted') {\n const notifObj = {}\n const status = notification.status\n const title = notification.from_profile.name\n notifObj.icon = notification.from_profile.profile_image_url\n let i18nString\n switch (notification.type) {\n case 'like':\n i18nString = 'favorited_you'\n break\n case 'repeat':\n i18nString = 'repeated_you'\n break\n case 'follow':\n i18nString = 'followed_you'\n break\n }\n\n if (i18nString) {\n notifObj.body = rootGetters.i18n.t('notifications.' + i18nString)\n } else {\n notifObj.body = notification.status.text\n }\n\n // Shows first attached non-nsfw image, if any. Should add configuration for this somehow...\n if (status && status.attachments && status.attachments.length > 0 && !status.nsfw &&\n status.attachments[0].mimetype.startsWith('image/')) {\n notifObj.image = status.attachments[0].url\n }\n\n if (!notification.seen && !state.notifications.desktopNotificationSilence && visibleNotificationTypes.includes(notification.type)) {\n let notification = new window.Notification(title, notifObj)\n // Chrome is known for not closing notifications automatically\n // according to MDN, anyway.\n setTimeout(notification.close.bind(notification), 5000)\n }\n }\n } else if (notification.seen) {\n state.notifications.idStore[notification.id].seen = true\n }\n })\n}\n\nconst removeStatus = (state, { timeline, userId }) => {\n const timelineObject = state.timelines[timeline]\n if (userId) {\n remove(timelineObject.statuses, { user: { id: userId } })\n remove(timelineObject.visibleStatuses, { user: { id: userId } })\n timelineObject.minVisibleId = timelineObject.visibleStatuses.length > 0 ? last(timelineObject.visibleStatuses).id : 0\n timelineObject.maxId = timelineObject.statuses.length > 0 ? first(timelineObject.statuses).id : 0\n }\n}\n\nexport const mutations = {\n addNewStatuses,\n addNewNotifications,\n removeStatus,\n showNewStatuses (state, { timeline }) {\n const oldTimeline = (state.timelines[timeline])\n\n oldTimeline.newStatusCount = 0\n oldTimeline.visibleStatuses = slice(oldTimeline.statuses, 0, 50)\n oldTimeline.minVisibleId = last(oldTimeline.visibleStatuses).id\n oldTimeline.minId = oldTimeline.minVisibleId\n oldTimeline.visibleStatusesObject = {}\n each(oldTimeline.visibleStatuses, (status) => { oldTimeline.visibleStatusesObject[status.id] = status })\n },\n resetStatuses (state) {\n const emptyState = defaultState()\n Object.entries(emptyState).forEach(([key, value]) => {\n state[key] = value\n })\n },\n clearTimeline (state, { timeline, excludeUserId = false }) {\n const userId = excludeUserId ? state.timelines[timeline].userId : undefined\n state.timelines[timeline] = emptyTl(userId)\n },\n clearNotifications (state) {\n state.notifications = emptyNotifications()\n },\n setFavorited (state, { status, value }) {\n const newStatus = state.allStatusesObject[status.id]\n\n if (newStatus.favorited !== value) {\n if (value) {\n newStatus.fave_num++\n } else {\n newStatus.fave_num--\n }\n }\n\n newStatus.favorited = value\n },\n setFavoritedConfirm (state, { status, user }) {\n const newStatus = state.allStatusesObject[status.id]\n newStatus.favorited = status.favorited\n newStatus.fave_num = status.fave_num\n const index = findIndex(newStatus.favoritedBy, { id: user.id })\n if (index !== -1 && !newStatus.favorited) {\n newStatus.favoritedBy.splice(index, 1)\n } else if (index === -1 && newStatus.favorited) {\n newStatus.favoritedBy.push(user)\n }\n },\n setMutedStatus (state, status) {\n const newStatus = state.allStatusesObject[status.id]\n newStatus.thread_muted = status.thread_muted\n\n if (newStatus.thread_muted !== undefined) {\n state.conversationsObject[newStatus.statusnet_conversation_id].forEach(status => { status.thread_muted = newStatus.thread_muted })\n }\n },\n setRetweeted (state, { status, value }) {\n const newStatus = state.allStatusesObject[status.id]\n\n if (newStatus.repeated !== value) {\n if (value) {\n newStatus.repeat_num++\n } else {\n newStatus.repeat_num--\n }\n }\n\n newStatus.repeated = value\n },\n setRetweetedConfirm (state, { status, user }) {\n const newStatus = state.allStatusesObject[status.id]\n newStatus.repeated = status.repeated\n newStatus.repeat_num = status.repeat_num\n const index = findIndex(newStatus.rebloggedBy, { id: user.id })\n if (index !== -1 && !newStatus.repeated) {\n newStatus.rebloggedBy.splice(index, 1)\n } else if (index === -1 && newStatus.repeated) {\n newStatus.rebloggedBy.push(user)\n }\n },\n setDeleted (state, { status }) {\n const newStatus = state.allStatusesObject[status.id]\n newStatus.deleted = true\n },\n setManyDeleted (state, condition) {\n Object.values(state.allStatusesObject).forEach(status => {\n if (condition(status)) {\n status.deleted = true\n }\n })\n },\n setLoading (state, { timeline, value }) {\n state.timelines[timeline].loading = value\n },\n setNsfw (state, { id, nsfw }) {\n const newStatus = state.allStatusesObject[id]\n newStatus.nsfw = nsfw\n },\n setError (state, { value }) {\n state.error = value\n },\n setNotificationsLoading (state, { value }) {\n state.notifications.loading = value\n },\n setNotificationsError (state, { value }) {\n state.notifications.error = value\n },\n setNotificationsSilence (state, { value }) {\n state.notifications.desktopNotificationSilence = value\n },\n markNotificationsAsSeen (state) {\n each(state.notifications.data, (notification) => {\n notification.seen = true\n })\n },\n queueFlush (state, { timeline, id }) {\n state.timelines[timeline].flushMarker = id\n },\n addRepeats (state, { id, rebloggedByUsers, currentUser }) {\n const newStatus = state.allStatusesObject[id]\n newStatus.rebloggedBy = rebloggedByUsers.filter(_ => _)\n // repeats stats can be incorrect based on polling condition, let's update them using the most recent data\n newStatus.repeat_num = newStatus.rebloggedBy.length\n newStatus.repeated = !!newStatus.rebloggedBy.find(({ id }) => currentUser.id === id)\n },\n addFavs (state, { id, favoritedByUsers, currentUser }) {\n const newStatus = state.allStatusesObject[id]\n newStatus.favoritedBy = favoritedByUsers.filter(_ => _)\n // favorites stats can be incorrect based on polling condition, let's update them using the most recent data\n newStatus.fave_num = newStatus.favoritedBy.length\n newStatus.favorited = !!newStatus.favoritedBy.find(({ id }) => currentUser.id === id)\n },\n updateStatusWithPoll (state, { id, poll }) {\n const status = state.allStatusesObject[id]\n status.poll = poll\n }\n}\n\nconst statuses = {\n state: defaultState(),\n actions: {\n addNewStatuses ({ rootState, commit }, { statuses, showImmediately = false, timeline = false, noIdUpdate = false, userId }) {\n commit('addNewStatuses', { statuses, showImmediately, timeline, noIdUpdate, user: rootState.users.currentUser, userId })\n },\n addNewNotifications ({ rootState, commit, dispatch, rootGetters }, { notifications, older }) {\n commit('addNewNotifications', { visibleNotificationTypes: visibleNotificationTypes(rootState), dispatch, notifications, older, rootGetters })\n },\n setError ({ rootState, commit }, { value }) {\n commit('setError', { value })\n },\n setNotificationsLoading ({ rootState, commit }, { value }) {\n commit('setNotificationsLoading', { value })\n },\n setNotificationsError ({ rootState, commit }, { value }) {\n commit('setNotificationsError', { value })\n },\n setNotificationsSilence ({ rootState, commit }, { value }) {\n commit('setNotificationsSilence', { value })\n },\n deleteStatus ({ rootState, commit }, status) {\n commit('setDeleted', { status })\n apiService.deleteStatus({ id: status.id, credentials: rootState.users.currentUser.credentials })\n },\n markStatusesAsDeleted ({ commit }, condition) {\n commit('setManyDeleted', condition)\n },\n favorite ({ rootState, commit }, status) {\n // Optimistic favoriting...\n commit('setFavorited', { status, value: true })\n rootState.api.backendInteractor.favorite(status.id)\n .then(status => commit('setFavoritedConfirm', { status, user: rootState.users.currentUser }))\n },\n unfavorite ({ rootState, commit }, status) {\n // Optimistic unfavoriting...\n commit('setFavorited', { status, value: false })\n rootState.api.backendInteractor.unfavorite(status.id)\n .then(status => commit('setFavoritedConfirm', { status, user: rootState.users.currentUser }))\n },\n fetchPinnedStatuses ({ rootState, dispatch }, userId) {\n rootState.api.backendInteractor.fetchPinnedStatuses(userId)\n .then(statuses => dispatch('addNewStatuses', { statuses, timeline: 'user', userId, showImmediately: true, noIdUpdate: true }))\n },\n pinStatus ({ rootState, dispatch }, statusId) {\n return rootState.api.backendInteractor.pinOwnStatus(statusId)\n .then((status) => dispatch('addNewStatuses', { statuses: [status] }))\n },\n unpinStatus ({ rootState, dispatch }, statusId) {\n rootState.api.backendInteractor.unpinOwnStatus(statusId)\n .then((status) => dispatch('addNewStatuses', { statuses: [status] }))\n },\n muteConversation ({ rootState, commit }, statusId) {\n return rootState.api.backendInteractor.muteConversation(statusId)\n .then((status) => commit('setMutedStatus', status))\n },\n unmuteConversation ({ rootState, commit }, statusId) {\n return rootState.api.backendInteractor.unmuteConversation(statusId)\n .then((status) => commit('setMutedStatus', status))\n },\n retweet ({ rootState, commit }, status) {\n // Optimistic retweeting...\n commit('setRetweeted', { status, value: true })\n rootState.api.backendInteractor.retweet(status.id)\n .then(status => commit('setRetweetedConfirm', { status: status.retweeted_status, user: rootState.users.currentUser }))\n },\n unretweet ({ rootState, commit }, status) {\n // Optimistic unretweeting...\n commit('setRetweeted', { status, value: false })\n rootState.api.backendInteractor.unretweet(status.id)\n .then(status => commit('setRetweetedConfirm', { status, user: rootState.users.currentUser }))\n },\n queueFlush ({ rootState, commit }, { timeline, id }) {\n commit('queueFlush', { timeline, id })\n },\n markNotificationsAsSeen ({ rootState, commit }) {\n commit('markNotificationsAsSeen')\n apiService.markNotificationsAsSeen({\n id: rootState.statuses.notifications.maxId,\n credentials: rootState.users.currentUser.credentials\n })\n },\n fetchFavsAndRepeats ({ rootState, commit }, id) {\n Promise.all([\n rootState.api.backendInteractor.fetchFavoritedByUsers(id),\n rootState.api.backendInteractor.fetchRebloggedByUsers(id)\n ]).then(([favoritedByUsers, rebloggedByUsers]) => {\n commit('addFavs', { id, favoritedByUsers, currentUser: rootState.users.currentUser })\n commit('addRepeats', { id, rebloggedByUsers, currentUser: rootState.users.currentUser })\n })\n },\n fetchFavs ({ rootState, commit }, id) {\n rootState.api.backendInteractor.fetchFavoritedByUsers(id)\n .then(favoritedByUsers => commit('addFavs', { id, favoritedByUsers, currentUser: rootState.users.currentUser }))\n },\n fetchRepeats ({ rootState, commit }, id) {\n rootState.api.backendInteractor.fetchRebloggedByUsers(id)\n .then(rebloggedByUsers => commit('addRepeats', { id, rebloggedByUsers, currentUser: rootState.users.currentUser }))\n },\n search (store, { q, resolve, limit, offset, following }) {\n return store.rootState.api.backendInteractor.search2({ q, resolve, limit, offset, following })\n .then((data) => {\n store.commit('addNewUsers', data.accounts)\n store.commit('addNewStatuses', { statuses: data.statuses })\n return data\n })\n }\n },\n mutations\n}\n\nexport default statuses\n","const qvitterStatusType = (status) => {\n if (status.is_post_verb) {\n return 'status'\n }\n\n if (status.retweeted_status) {\n return 'retweet'\n }\n\n if ((typeof status.uri === 'string' && status.uri.match(/(fave|objectType=Favourite)/)) ||\n (typeof status.text === 'string' && status.text.match(/favorited/))) {\n return 'favorite'\n }\n\n if (status.text.match(/deleted notice {{tag/) || status.qvitter_delete_notice) {\n return 'deletion'\n }\n\n if (status.text.match(/started following/) || status.activity_type === 'follow') {\n return 'follow'\n }\n\n return 'unknown'\n}\n\nexport const parseUser = (data) => {\n const output = {}\n const masto = data.hasOwnProperty('acct')\n // case for users in \"mentions\" property for statuses in MastoAPI\n const mastoShort = masto && !data.hasOwnProperty('avatar')\n\n output.id = String(data.id)\n\n if (masto) {\n output.screen_name = data.acct\n output.statusnet_profile_url = data.url\n\n // There's nothing else to get\n if (mastoShort) {\n return output\n }\n\n output.name = data.display_name\n output.name_html = addEmojis(data.display_name, data.emojis)\n\n output.description = data.note\n output.description_html = addEmojis(data.note, data.emojis)\n\n // Utilize avatar_static for gif avatars?\n output.profile_image_url = data.avatar\n output.profile_image_url_original = data.avatar\n\n // Same, utilize header_static?\n output.cover_photo = data.header\n\n output.friends_count = data.following_count\n\n output.bot = data.bot\n\n if (data.pleroma) {\n const relationship = data.pleroma.relationship\n\n output.background_image = data.pleroma.background_image\n output.token = data.pleroma.chat_token\n\n if (relationship) {\n output.follows_you = relationship.followed_by\n output.requested = relationship.requested\n output.following = relationship.following\n output.statusnet_blocking = relationship.blocking\n output.muted = relationship.muting\n output.subscribed = relationship.subscribing\n }\n\n output.hide_follows = data.pleroma.hide_follows\n output.hide_followers = data.pleroma.hide_followers\n output.hide_follows_count = data.pleroma.hide_follows_count\n output.hide_followers_count = data.pleroma.hide_followers_count\n\n output.rights = {\n moderator: data.pleroma.is_moderator,\n admin: data.pleroma.is_admin\n }\n // TODO: Clean up in UI? This is duplication from what BE does for qvitterapi\n if (output.rights.admin) {\n output.role = 'admin'\n } else if (output.rights.moderator) {\n output.role = 'moderator'\n } else {\n output.role = 'member'\n }\n }\n\n if (data.source) {\n output.description = data.source.note\n output.default_scope = data.source.privacy\n if (data.source.pleroma) {\n output.no_rich_text = data.source.pleroma.no_rich_text\n output.show_role = data.source.pleroma.show_role\n output.discoverable = data.source.pleroma.discoverable\n }\n }\n\n // TODO: handle is_local\n output.is_local = !output.screen_name.includes('@')\n } else {\n output.screen_name = data.screen_name\n\n output.name = data.name\n output.name_html = data.name_html\n\n output.description = data.description\n output.description_html = data.description_html\n\n output.profile_image_url = data.profile_image_url\n output.profile_image_url_original = data.profile_image_url_original\n\n output.cover_photo = data.cover_photo\n\n output.friends_count = data.friends_count\n\n // output.bot = ??? missing\n\n output.statusnet_profile_url = data.statusnet_profile_url\n\n output.statusnet_blocking = data.statusnet_blocking\n\n output.is_local = data.is_local\n output.role = data.role\n output.show_role = data.show_role\n\n output.follows_you = data.follows_you\n\n output.muted = data.muted\n\n if (data.rights) {\n output.rights = {\n moderator: data.rights.delete_others_notice,\n admin: data.rights.admin\n }\n }\n output.no_rich_text = data.no_rich_text\n output.default_scope = data.default_scope\n output.hide_follows = data.hide_follows\n output.hide_followers = data.hide_followers\n output.hide_follows_count = data.hide_follows_count\n output.hide_followers_count = data.hide_followers_count\n output.background_image = data.background_image\n // on mastoapi this info is contained in a \"relationship\"\n output.following = data.following\n // Websocket token\n output.token = data.token\n }\n\n output.created_at = new Date(data.created_at)\n output.locked = data.locked\n output.followers_count = data.followers_count\n output.statuses_count = data.statuses_count\n output.friendIds = []\n output.followerIds = []\n output.pinnedStatusIds = []\n\n if (data.pleroma) {\n output.follow_request_count = data.pleroma.follow_request_count\n\n output.tags = data.pleroma.tags\n output.deactivated = data.pleroma.deactivated\n\n output.notification_settings = data.pleroma.notification_settings\n }\n\n output.tags = output.tags || []\n output.rights = output.rights || {}\n output.notification_settings = output.notification_settings || {}\n\n return output\n}\n\nexport const parseAttachment = (data) => {\n const output = {}\n const masto = !data.hasOwnProperty('oembed')\n\n if (masto) {\n // Not exactly same...\n output.mimetype = data.pleroma ? data.pleroma.mime_type : data.type\n output.meta = data.meta // not present in BE yet\n output.id = data.id\n } else {\n output.mimetype = data.mimetype\n // output.meta = ??? missing\n }\n\n output.url = data.url\n output.description = data.description\n\n return output\n}\nexport const addEmojis = (string, emojis) => {\n const matchOperatorsRegex = /[|\\\\{}()[\\]^$+*?.-]/g\n return emojis.reduce((acc, emoji) => {\n const regexSafeShortCode = emoji.shortcode.replace(matchOperatorsRegex, '\\\\$&')\n return acc.replace(\n new RegExp(`:${regexSafeShortCode}:`, 'g'),\n `${emoji.shortcode}`\n )\n }, string)\n}\n\nexport const parseStatus = (data) => {\n const output = {}\n const masto = data.hasOwnProperty('account')\n\n if (masto) {\n output.favorited = data.favourited\n output.fave_num = data.favourites_count\n\n output.repeated = data.reblogged\n output.repeat_num = data.reblogs_count\n\n output.type = data.reblog ? 'retweet' : 'status'\n output.nsfw = data.sensitive\n\n output.statusnet_html = addEmojis(data.content, data.emojis)\n\n output.tags = data.tags\n\n if (data.pleroma) {\n const { pleroma } = data\n output.text = pleroma.content ? data.pleroma.content['text/plain'] : data.content\n output.summary = pleroma.spoiler_text ? data.pleroma.spoiler_text['text/plain'] : data.spoiler_text\n output.statusnet_conversation_id = data.pleroma.conversation_id\n output.is_local = pleroma.local\n output.in_reply_to_screen_name = data.pleroma.in_reply_to_account_acct\n output.thread_muted = pleroma.thread_muted\n } else {\n output.text = data.content\n output.summary = data.spoiler_text\n }\n\n output.in_reply_to_status_id = data.in_reply_to_id\n output.in_reply_to_user_id = data.in_reply_to_account_id\n output.replies_count = data.replies_count\n\n if (output.type === 'retweet') {\n output.retweeted_status = parseStatus(data.reblog)\n }\n\n output.summary_html = addEmojis(data.spoiler_text, data.emojis)\n output.external_url = data.url\n output.poll = data.poll\n output.pinned = data.pinned\n output.muted = data.muted\n } else {\n output.favorited = data.favorited\n output.fave_num = data.fave_num\n\n output.repeated = data.repeated\n output.repeat_num = data.repeat_num\n\n // catchall, temporary\n // Object.assign(output, data)\n\n output.type = qvitterStatusType(data)\n\n if (data.nsfw === undefined) {\n output.nsfw = isNsfw(data)\n if (data.retweeted_status) {\n output.nsfw = data.retweeted_status.nsfw\n }\n } else {\n output.nsfw = data.nsfw\n }\n\n output.statusnet_html = data.statusnet_html\n output.text = data.text\n\n output.in_reply_to_status_id = data.in_reply_to_status_id\n output.in_reply_to_user_id = data.in_reply_to_user_id\n output.in_reply_to_screen_name = data.in_reply_to_screen_name\n output.statusnet_conversation_id = data.statusnet_conversation_id\n\n if (output.type === 'retweet') {\n output.retweeted_status = parseStatus(data.retweeted_status)\n }\n\n output.summary = data.summary\n output.summary_html = data.summary_html\n output.external_url = data.external_url\n output.is_local = data.is_local\n }\n\n output.id = String(data.id)\n output.visibility = data.visibility\n output.card = data.card\n output.created_at = new Date(data.created_at)\n\n // Converting to string, the right way.\n output.in_reply_to_status_id = output.in_reply_to_status_id\n ? String(output.in_reply_to_status_id)\n : null\n output.in_reply_to_user_id = output.in_reply_to_user_id\n ? String(output.in_reply_to_user_id)\n : null\n\n output.user = parseUser(masto ? data.account : data.user)\n\n output.attentions = ((masto ? data.mentions : data.attentions) || []).map(parseUser)\n\n output.attachments = ((masto ? data.media_attachments : data.attachments) || [])\n .map(parseAttachment)\n\n const retweetedStatus = masto ? data.reblog : data.retweeted_status\n if (retweetedStatus) {\n output.retweeted_status = parseStatus(retweetedStatus)\n }\n\n output.favoritedBy = []\n output.rebloggedBy = []\n\n return output\n}\n\nexport const parseNotification = (data) => {\n const mastoDict = {\n 'favourite': 'like',\n 'reblog': 'repeat'\n }\n const masto = !data.hasOwnProperty('ntype')\n const output = {}\n\n if (masto) {\n output.type = mastoDict[data.type] || data.type\n output.seen = data.pleroma.is_seen\n output.status = output.type === 'follow'\n ? null\n : parseStatus(data.status)\n output.action = output.status // TODO: Refactor, this is unneeded\n output.from_profile = parseUser(data.account)\n } else {\n const parsedNotice = parseStatus(data.notice)\n output.type = data.ntype\n output.seen = Boolean(data.is_seen)\n output.status = output.type === 'like'\n ? parseStatus(data.notice.favorited_status)\n : parsedNotice\n output.action = parsedNotice\n output.from_profile = parseUser(data.from_profile)\n }\n\n output.created_at = new Date(data.created_at)\n output.id = parseInt(data.id)\n\n return output\n}\n\nconst isNsfw = (status) => {\n const nsfwRegex = /#nsfw/i\n return (status.tags || []).includes('nsfw') || !!(status.text || '').match(nsfwRegex)\n}\n","import { humanizeErrors } from '../../modules/errors'\n\nexport function StatusCodeError (statusCode, body, options, response) {\n this.name = 'StatusCodeError'\n this.statusCode = statusCode\n this.message = statusCode + ' - ' + (JSON && JSON.stringify ? JSON.stringify(body) : body)\n this.error = body // legacy attribute\n this.options = options\n this.response = response\n\n if (Error.captureStackTrace) { // required for non-V8 environments\n Error.captureStackTrace(this)\n }\n}\nStatusCodeError.prototype = Object.create(Error.prototype)\nStatusCodeError.prototype.constructor = StatusCodeError\n\nexport class RegistrationError extends Error {\n constructor (error) {\n super()\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this)\n }\n\n try {\n // the error is probably a JSON object with a single key, \"errors\", whose value is another JSON object containing the real errors\n if (typeof error === 'string') {\n error = JSON.parse(error)\n if (error.hasOwnProperty('error')) {\n error = JSON.parse(error.error)\n }\n }\n\n if (typeof error === 'object') {\n // replace ap_id with username\n if (error.ap_id) {\n error.username = error.ap_id\n delete error.ap_id\n }\n this.message = humanizeErrors(error)\n } else {\n this.message = error\n }\n } catch (e) {\n // can't parse it, so just treat it like a string\n this.message = error\n }\n }\n}\n","import { capitalize } from 'lodash'\n\nexport function humanizeErrors (errors) {\n return Object.entries(errors).reduce((errs, [k, val]) => {\n let message = val.reduce((acc, message) => {\n let key = capitalize(k.replace(/_/g, ' '))\n return acc + [key, message].join(' ') + '. '\n }, '')\n return [...errs, message]\n }, [])\n}\n","import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js'\nimport oauthApi from '../services/new_api/oauth.js'\nimport { compact, map, each, merge, last, concat, uniq } from 'lodash'\nimport { set } from 'vue'\nimport { registerPushNotifications, unregisterPushNotifications } from '../services/push/push.js'\n\n// TODO: Unify with mergeOrAdd in statuses.js\nexport const mergeOrAdd = (arr, obj, item) => {\n if (!item) { return false }\n const oldItem = obj[item.id]\n if (oldItem) {\n // We already have this, so only merge the new info.\n merge(oldItem, item)\n return { item: oldItem, new: false }\n } else {\n // This is a new item, prepare it\n arr.push(item)\n set(obj, item.id, item)\n if (item.screen_name && !item.screen_name.includes('@')) {\n set(obj, item.screen_name.toLowerCase(), item)\n }\n return { item, new: true }\n }\n}\n\nconst getNotificationPermission = () => {\n const Notification = window.Notification\n\n if (!Notification) return Promise.resolve(null)\n if (Notification.permission === 'default') return Notification.requestPermission()\n return Promise.resolve(Notification.permission)\n}\n\nconst blockUser = (store, id) => {\n return store.rootState.api.backendInteractor.blockUser(id)\n .then((relationship) => {\n store.commit('updateUserRelationship', [relationship])\n store.commit('addBlockId', id)\n store.commit('removeStatus', { timeline: 'friends', userId: id })\n store.commit('removeStatus', { timeline: 'public', userId: id })\n store.commit('removeStatus', { timeline: 'publicAndExternal', userId: id })\n })\n}\n\nconst unblockUser = (store, id) => {\n return store.rootState.api.backendInteractor.unblockUser(id)\n .then((relationship) => store.commit('updateUserRelationship', [relationship]))\n}\n\nconst muteUser = (store, id) => {\n return store.rootState.api.backendInteractor.muteUser(id)\n .then((relationship) => {\n store.commit('updateUserRelationship', [relationship])\n store.commit('addMuteId', id)\n })\n}\n\nconst unmuteUser = (store, id) => {\n return store.rootState.api.backendInteractor.unmuteUser(id)\n .then((relationship) => store.commit('updateUserRelationship', [relationship]))\n}\n\nexport const mutations = {\n setMuted (state, { user: { id }, muted }) {\n const user = state.usersObject[id]\n set(user, 'muted', muted)\n },\n tagUser (state, { user: { id }, tag }) {\n const user = state.usersObject[id]\n const tags = user.tags || []\n const newTags = tags.concat([tag])\n set(user, 'tags', newTags)\n },\n untagUser (state, { user: { id }, tag }) {\n const user = state.usersObject[id]\n const tags = user.tags || []\n const newTags = tags.filter(t => t !== tag)\n set(user, 'tags', newTags)\n },\n updateRight (state, { user: { id }, right, value }) {\n const user = state.usersObject[id]\n let newRights = user.rights\n newRights[right] = value\n set(user, 'rights', newRights)\n },\n updateActivationStatus (state, { user: { id }, status }) {\n const user = state.usersObject[id]\n set(user, 'deactivated', !status)\n },\n setCurrentUser (state, user) {\n state.lastLoginName = user.screen_name\n state.currentUser = merge(state.currentUser || {}, user)\n },\n clearCurrentUser (state) {\n state.currentUser = false\n state.lastLoginName = false\n },\n beginLogin (state) {\n state.loggingIn = true\n },\n endLogin (state) {\n state.loggingIn = false\n },\n saveFriendIds (state, { id, friendIds }) {\n const user = state.usersObject[id]\n user.friendIds = uniq(concat(user.friendIds, friendIds))\n },\n saveFollowerIds (state, { id, followerIds }) {\n const user = state.usersObject[id]\n user.followerIds = uniq(concat(user.followerIds, followerIds))\n },\n // Because frontend doesn't have a reason to keep these stuff in memory\n // outside of viewing someones user profile.\n clearFriends (state, userId) {\n const user = state.usersObject[userId]\n if (user) {\n set(user, 'friendIds', [])\n }\n },\n clearFollowers (state, userId) {\n const user = state.usersObject[userId]\n if (user) {\n set(user, 'followerIds', [])\n }\n },\n addNewUsers (state, users) {\n each(users, (user) => mergeOrAdd(state.users, state.usersObject, user))\n },\n updateUserRelationship (state, relationships) {\n relationships.forEach((relationship) => {\n const user = state.usersObject[relationship.id]\n if (user) {\n user.follows_you = relationship.followed_by\n user.following = relationship.following\n user.muted = relationship.muting\n user.statusnet_blocking = relationship.blocking\n user.subscribed = relationship.subscribing\n }\n })\n },\n updateBlocks (state, blockedUsers) {\n // Reset statusnet_blocking of all fetched users\n each(state.users, (user) => { user.statusnet_blocking = false })\n each(blockedUsers, (user) => mergeOrAdd(state.users, state.usersObject, user))\n },\n saveBlockIds (state, blockIds) {\n state.currentUser.blockIds = blockIds\n },\n addBlockId (state, blockId) {\n if (state.currentUser.blockIds.indexOf(blockId) === -1) {\n state.currentUser.blockIds.push(blockId)\n }\n },\n updateMutes (state, mutedUsers) {\n // Reset muted of all fetched users\n each(state.users, (user) => { user.muted = false })\n each(mutedUsers, (user) => mergeOrAdd(state.users, state.usersObject, user))\n },\n saveMuteIds (state, muteIds) {\n state.currentUser.muteIds = muteIds\n },\n addMuteId (state, muteId) {\n if (state.currentUser.muteIds.indexOf(muteId) === -1) {\n state.currentUser.muteIds.push(muteId)\n }\n },\n setPinnedToUser (state, status) {\n const user = state.usersObject[status.user.id]\n const index = user.pinnedStatusIds.indexOf(status.id)\n if (status.pinned && index === -1) {\n user.pinnedStatusIds.push(status.id)\n } else if (!status.pinned && index !== -1) {\n user.pinnedStatusIds.splice(index, 1)\n }\n },\n setUserForStatus (state, status) {\n status.user = state.usersObject[status.user.id]\n },\n setUserForNotification (state, notification) {\n if (notification.type !== 'follow') {\n notification.action.user = state.usersObject[notification.action.user.id]\n }\n notification.from_profile = state.usersObject[notification.from_profile.id]\n },\n setColor (state, { user: { id }, highlighted }) {\n const user = state.usersObject[id]\n set(user, 'highlight', highlighted)\n },\n signUpPending (state) {\n state.signUpPending = true\n state.signUpErrors = []\n },\n signUpSuccess (state) {\n state.signUpPending = false\n },\n signUpFailure (state, errors) {\n state.signUpPending = false\n state.signUpErrors = errors\n }\n}\n\nexport const getters = {\n findUser: state => query => {\n const result = state.usersObject[query]\n // In case it's a screen_name, we can try searching case-insensitive\n if (!result && typeof query === 'string') {\n return state.usersObject[query.toLowerCase()]\n }\n return result\n }\n}\n\nexport const defaultState = {\n loggingIn: false,\n lastLoginName: false,\n currentUser: false,\n users: [],\n usersObject: {},\n signUpPending: false,\n signUpErrors: []\n}\n\nconst users = {\n state: defaultState,\n mutations,\n getters,\n actions: {\n fetchUser (store, id) {\n return store.rootState.api.backendInteractor.fetchUser({ id })\n .then((user) => {\n store.commit('addNewUsers', [user])\n return user\n })\n },\n fetchUserRelationship (store, id) {\n if (store.state.currentUser) {\n store.rootState.api.backendInteractor.fetchUserRelationship({ id })\n .then((relationships) => store.commit('updateUserRelationship', relationships))\n }\n },\n fetchBlocks (store) {\n return store.rootState.api.backendInteractor.fetchBlocks()\n .then((blocks) => {\n store.commit('saveBlockIds', map(blocks, 'id'))\n store.commit('updateBlocks', blocks)\n return blocks\n })\n },\n blockUser (store, id) {\n return blockUser(store, id)\n },\n unblockUser (store, id) {\n return unblockUser(store, id)\n },\n blockUsers (store, ids = []) {\n return Promise.all(ids.map(id => blockUser(store, id)))\n },\n unblockUsers (store, ids = []) {\n return Promise.all(ids.map(id => unblockUser(store, id)))\n },\n fetchMutes (store) {\n return store.rootState.api.backendInteractor.fetchMutes()\n .then((mutes) => {\n store.commit('updateMutes', mutes)\n store.commit('saveMuteIds', map(mutes, 'id'))\n return mutes\n })\n },\n muteUser (store, id) {\n return muteUser(store, id)\n },\n unmuteUser (store, id) {\n return unmuteUser(store, id)\n },\n muteUsers (store, ids = []) {\n return Promise.all(ids.map(id => muteUser(store, id)))\n },\n unmuteUsers (store, ids = []) {\n return Promise.all(ids.map(id => unmuteUser(store, id)))\n },\n fetchFriends ({ rootState, commit }, id) {\n const user = rootState.users.usersObject[id]\n const maxId = last(user.friendIds)\n return rootState.api.backendInteractor.fetchFriends({ id, maxId })\n .then((friends) => {\n commit('addNewUsers', friends)\n commit('saveFriendIds', { id, friendIds: map(friends, 'id') })\n return friends\n })\n },\n fetchFollowers ({ rootState, commit }, id) {\n const user = rootState.users.usersObject[id]\n const maxId = last(user.followerIds)\n return rootState.api.backendInteractor.fetchFollowers({ id, maxId })\n .then((followers) => {\n commit('addNewUsers', followers)\n commit('saveFollowerIds', { id, followerIds: map(followers, 'id') })\n return followers\n })\n },\n clearFriends ({ commit }, userId) {\n commit('clearFriends', userId)\n },\n clearFollowers ({ commit }, userId) {\n commit('clearFollowers', userId)\n },\n subscribeUser ({ rootState, commit }, id) {\n return rootState.api.backendInteractor.subscribeUser(id)\n .then((relationship) => commit('updateUserRelationship', [relationship]))\n },\n unsubscribeUser ({ rootState, commit }, id) {\n return rootState.api.backendInteractor.unsubscribeUser(id)\n .then((relationship) => commit('updateUserRelationship', [relationship]))\n },\n registerPushNotifications (store) {\n const token = store.state.currentUser.credentials\n const vapidPublicKey = store.rootState.instance.vapidPublicKey\n const isEnabled = store.rootState.config.webPushNotifications\n const notificationVisibility = store.rootState.config.notificationVisibility\n\n registerPushNotifications(isEnabled, vapidPublicKey, token, notificationVisibility)\n },\n unregisterPushNotifications (store) {\n const token = store.state.currentUser.credentials\n\n unregisterPushNotifications(token)\n },\n addNewUsers ({ commit }, users) {\n commit('addNewUsers', users)\n },\n addNewStatuses (store, { statuses }) {\n const users = map(statuses, 'user')\n const retweetedUsers = compact(map(statuses, 'retweeted_status.user'))\n store.commit('addNewUsers', users)\n store.commit('addNewUsers', retweetedUsers)\n\n each(statuses, (status) => {\n // Reconnect users to statuses\n store.commit('setUserForStatus', status)\n // Set pinned statuses to user\n store.commit('setPinnedToUser', status)\n })\n each(compact(map(statuses, 'retweeted_status')), (status) => {\n // Reconnect users to retweets\n store.commit('setUserForStatus', status)\n // Set pinned retweets to user\n store.commit('setPinnedToUser', status)\n })\n },\n addNewNotifications (store, { notifications }) {\n const users = map(notifications, 'from_profile')\n const notificationIds = notifications.map(_ => _.id)\n store.commit('addNewUsers', users)\n\n const notificationsObject = store.rootState.statuses.notifications.idStore\n const relevantNotifications = Object.entries(notificationsObject)\n .filter(([k, val]) => notificationIds.includes(k))\n .map(([k, val]) => val)\n\n // Reconnect users to notifications\n each(relevantNotifications, (notification) => {\n store.commit('setUserForNotification', notification)\n })\n },\n searchUsers (store, query) {\n return store.rootState.api.backendInteractor.searchUsers(query)\n .then((users) => {\n store.commit('addNewUsers', users)\n return users\n })\n },\n async signUp (store, userInfo) {\n store.commit('signUpPending')\n\n let rootState = store.rootState\n\n try {\n let data = await rootState.api.backendInteractor.register(userInfo)\n store.commit('signUpSuccess')\n store.commit('setToken', data.access_token)\n store.dispatch('loginUser', data.access_token)\n } catch (e) {\n let errors = e.message\n store.commit('signUpFailure', errors)\n throw e\n }\n },\n async getCaptcha (store) {\n return store.rootState.api.backendInteractor.getCaptcha()\n },\n\n logout (store) {\n const { oauth, instance } = store.rootState\n\n const data = {\n ...oauth,\n commit: store.commit,\n instance: instance.server\n }\n\n return oauthApi.getOrCreateApp(data)\n .then((app) => {\n const params = {\n app,\n instance: data.instance,\n token: oauth.userToken\n }\n\n return oauthApi.revokeToken(params)\n })\n .then(() => {\n store.commit('clearCurrentUser')\n store.dispatch('disconnectFromSocket')\n store.commit('clearToken')\n store.dispatch('stopFetching', 'friends')\n store.commit('setBackendInteractor', backendInteractorService(store.getters.getToken()))\n store.dispatch('stopFetching', 'notifications')\n store.commit('clearNotifications')\n store.commit('resetStatuses')\n })\n },\n loginUser (store, accessToken) {\n return new Promise((resolve, reject) => {\n const commit = store.commit\n commit('beginLogin')\n store.rootState.api.backendInteractor.verifyCredentials(accessToken)\n .then((data) => {\n if (!data.error) {\n const user = data\n // user.credentials = userCredentials\n user.credentials = accessToken\n user.blockIds = []\n user.muteIds = []\n commit('setCurrentUser', user)\n commit('addNewUsers', [user])\n\n getNotificationPermission()\n .then(permission => commit('setNotificationPermission', permission))\n\n // Set our new backend interactor\n commit('setBackendInteractor', backendInteractorService(accessToken))\n\n if (user.token) {\n store.dispatch('setWsToken', user.token)\n\n // Initialize the chat socket.\n store.dispatch('initializeSocket')\n }\n\n // Start getting fresh posts.\n store.dispatch('startFetchingTimeline', { timeline: 'friends' })\n\n // Start fetching notifications\n store.dispatch('startFetchingNotifications')\n\n // Get user mutes\n store.dispatch('fetchMutes')\n\n // Fetch our friends\n store.rootState.api.backendInteractor.fetchFriends({ id: user.id })\n .then((friends) => commit('addNewUsers', friends))\n } else {\n const response = data.error\n // Authentication failed\n commit('endLogin')\n if (response.status === 401) {\n reject(new Error('Wrong username or password'))\n } else {\n reject(new Error('An error occurred, please try again'))\n }\n }\n commit('endLogin')\n resolve()\n })\n .catch((error) => {\n console.log(error)\n commit('endLogin')\n reject(new Error('Failed to connect to server, try again'))\n })\n })\n }\n }\n}\n\nexport default users\n","import runtime from 'serviceworker-webpack-plugin/lib/runtime'\n\nfunction urlBase64ToUint8Array (base64String) {\n const padding = '='.repeat((4 - base64String.length % 4) % 4)\n const base64 = (base64String + padding)\n .replace(/-/g, '+')\n .replace(/_/g, '/')\n\n const rawData = window.atob(base64)\n return Uint8Array.from([...rawData].map((char) => char.charCodeAt(0)))\n}\n\nfunction isPushSupported () {\n return 'serviceWorker' in navigator && 'PushManager' in window\n}\n\nfunction getOrCreateServiceWorker () {\n return runtime.register()\n .catch((err) => console.error('Unable to get or create a service worker.', err))\n}\n\nfunction subscribePush (registration, isEnabled, vapidPublicKey) {\n if (!isEnabled) return Promise.reject(new Error('Web Push is disabled in config'))\n if (!vapidPublicKey) return Promise.reject(new Error('VAPID public key is not found'))\n\n const subscribeOptions = {\n userVisibleOnly: true,\n applicationServerKey: urlBase64ToUint8Array(vapidPublicKey)\n }\n return registration.pushManager.subscribe(subscribeOptions)\n}\n\nfunction unsubscribePush (registration) {\n return registration.pushManager.getSubscription()\n .then((subscribtion) => {\n if (subscribtion === null) { return }\n return subscribtion.unsubscribe()\n })\n}\n\nfunction deleteSubscriptionFromBackEnd (token) {\n return window.fetch('/api/v1/push/subscription/', {\n method: 'DELETE',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${token}`\n }\n }).then((response) => {\n if (!response.ok) throw new Error('Bad status code from server.')\n return response\n })\n}\n\nfunction sendSubscriptionToBackEnd (subscription, token, notificationVisibility) {\n return window.fetch('/api/v1/push/subscription/', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${token}`\n },\n body: JSON.stringify({\n subscription,\n data: {\n alerts: {\n follow: notificationVisibility.follows,\n favourite: notificationVisibility.likes,\n mention: notificationVisibility.mentions,\n reblog: notificationVisibility.repeats\n }\n }\n })\n }).then((response) => {\n if (!response.ok) throw new Error('Bad status code from server.')\n return response.json()\n }).then((responseData) => {\n if (!responseData.id) throw new Error('Bad response from server.')\n return responseData\n })\n}\n\nexport function registerPushNotifications (isEnabled, vapidPublicKey, token, notificationVisibility) {\n if (isPushSupported()) {\n getOrCreateServiceWorker()\n .then((registration) => subscribePush(registration, isEnabled, vapidPublicKey))\n .then((subscription) => sendSubscriptionToBackEnd(subscription, token, notificationVisibility))\n .catch((e) => console.warn(`Failed to setup Web Push Notifications: ${e.message}`))\n }\n}\n\nexport function unregisterPushNotifications (token) {\n if (isPushSupported()) {\n Promise.all([\n deleteSubscriptionFromBackEnd(token),\n getOrCreateServiceWorker()\n .then((registration) => {\n return unsubscribePush(registration).then((result) => [registration, result])\n })\n .then(([registration, unsubResult]) => {\n if (!unsubResult) {\n console.warn('Push subscription cancellation wasn\\'t successful, killing SW anyway...')\n }\n return registration.unregister().then((result) => {\n if (!result) {\n console.warn('Failed to kill SW')\n }\n })\n })\n ]).catch((e) => console.warn(`Failed to disable Web Push Notifications: ${e.message}`))\n }\n}\n","import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js'\nimport { Socket } from 'phoenix'\n\nconst api = {\n state: {\n backendInteractor: backendInteractorService(),\n fetchers: {},\n socket: null,\n followRequests: []\n },\n mutations: {\n setBackendInteractor (state, backendInteractor) {\n state.backendInteractor = backendInteractor\n },\n addFetcher (state, { fetcherName, fetcher }) {\n state.fetchers[fetcherName] = fetcher\n },\n removeFetcher (state, { fetcherName }) {\n delete state.fetchers[fetcherName]\n },\n setWsToken (state, token) {\n state.wsToken = token\n },\n setSocket (state, socket) {\n state.socket = socket\n },\n setFollowRequests (state, value) {\n state.followRequests = value\n }\n },\n actions: {\n startFetchingTimeline (store, { timeline = 'friends', tag = false, userId = false }) {\n // Don't start fetching if we already are.\n if (store.state.fetchers[timeline]) return\n\n const fetcher = store.state.backendInteractor.startFetchingTimeline({ timeline, store, userId, tag })\n store.commit('addFetcher', { fetcherName: timeline, fetcher })\n },\n startFetchingNotifications (store) {\n // Don't start fetching if we already are.\n if (store.state.fetchers['notifications']) return\n\n const fetcher = store.state.backendInteractor.startFetchingNotifications({ store })\n store.commit('addFetcher', { fetcherName: 'notifications', fetcher })\n },\n stopFetching (store, fetcherName) {\n const fetcher = store.state.fetchers[fetcherName]\n window.clearInterval(fetcher)\n store.commit('removeFetcher', { fetcherName })\n },\n setWsToken (store, token) {\n store.commit('setWsToken', token)\n },\n initializeSocket ({ dispatch, commit, state, rootState }) {\n // Set up websocket connection\n const token = state.wsToken\n if (rootState.instance.chatAvailable && typeof token !== 'undefined' && state.socket === null) {\n const socket = new Socket('/socket', { params: { token } })\n socket.connect()\n\n commit('setSocket', socket)\n dispatch('initializeChat', socket)\n }\n },\n disconnectFromSocket ({ commit, state }) {\n state.socket && state.socket.disconnect()\n commit('setSocket', null)\n },\n removeFollowRequest (store, request) {\n let requests = store.state.followRequests.filter((it) => it !== request)\n store.commit('setFollowRequests', requests)\n }\n }\n}\n\nexport default api\n","import { set, delete as del } from 'vue'\nimport { setPreset, applyTheme } from '../services/style_setter/style_setter.js'\n\nconst browserLocale = (window.navigator.language || 'en').split('-')[0]\n\nconst defaultState = {\n colors: {},\n hideMutedPosts: undefined, // instance default\n collapseMessageWithSubject: undefined, // instance default\n padEmoji: true,\n hideAttachments: false,\n hideAttachmentsInConv: false,\n maxThumbnails: 16,\n hideNsfw: true,\n preloadImage: true,\n loopVideo: true,\n loopVideoSilentOnly: true,\n autoLoad: true,\n streaming: false,\n hoverPreview: true,\n autohideFloatingPostButton: false,\n pauseOnUnfocused: true,\n stopGifs: false,\n replyVisibility: 'all',\n notificationVisibility: {\n follows: true,\n mentions: true,\n likes: true,\n repeats: true\n },\n webPushNotifications: false,\n muteWords: [],\n highlight: {},\n interfaceLanguage: browserLocale,\n hideScopeNotice: false,\n scopeCopy: undefined, // instance default\n subjectLineBehavior: undefined, // instance default\n alwaysShowSubjectInput: undefined, // instance default\n postContentType: undefined, // instance default\n minimalScopesMode: undefined // instance default\n}\n\nconst config = {\n state: defaultState,\n mutations: {\n setOption (state, { name, value }) {\n set(state, name, value)\n },\n setHighlight (state, { user, color, type }) {\n const data = this.state.config.highlight[user]\n if (color || type) {\n set(state.highlight, user, { color: color || data.color, type: type || data.type })\n } else {\n del(state.highlight, user)\n }\n }\n },\n actions: {\n setHighlight ({ commit, dispatch }, { user, color, type }) {\n commit('setHighlight', { user, color, type })\n },\n setOption ({ commit, dispatch }, { name, value }) {\n commit('setOption', { name, value })\n switch (name) {\n case 'theme':\n setPreset(value, commit)\n break\n case 'customTheme':\n applyTheme(value, commit)\n }\n }\n }\n}\n\nexport default config\n","const chat = {\n state: {\n messages: [],\n channel: { state: '' }\n },\n mutations: {\n setChannel (state, channel) {\n state.channel = channel\n },\n addMessage (state, message) {\n state.messages.push(message)\n state.messages = state.messages.slice(-19, 20)\n },\n setMessages (state, messages) {\n state.messages = messages.slice(-19, 20)\n }\n },\n actions: {\n initializeChat (store, socket) {\n const channel = socket.channel('chat:public')\n channel.on('new_msg', (msg) => {\n store.commit('addMessage', msg)\n })\n channel.on('messages', ({ messages }) => {\n store.commit('setMessages', messages)\n })\n channel.join()\n store.commit('setChannel', channel)\n }\n }\n}\n\nexport default chat\n","import { delete as del } from 'vue'\n\nconst oauth = {\n state: {\n clientId: false,\n clientSecret: false,\n /* App token is authentication for app without any user, used mostly for\n * MastoAPI's registration of new users, stored so that we can fall back to\n * it on logout\n */\n appToken: false,\n /* User token is authentication for app with user, this is for every calls\n * that need authorized user to be successful (i.e. posting, liking etc)\n */\n userToken: false\n },\n mutations: {\n setClientData (state, { clientId, clientSecret }) {\n state.clientId = clientId\n state.clientSecret = clientSecret\n },\n setAppToken (state, token) {\n state.appToken = token\n },\n setToken (state, token) {\n state.userToken = token\n },\n clearToken (state) {\n state.userToken = false\n // state.token is userToken with older name, coming from persistent state\n // let's clear it as well, since it is being used as a fallback of state.userToken\n del(state, 'token')\n }\n },\n getters: {\n getToken: state => () => {\n // state.token is userToken with older name, coming from persistent state\n // added here for smoother transition, otherwise user will be logged out\n return state.userToken || state.token || state.appToken\n },\n getUserToken: state => () => {\n // state.token is userToken with older name, coming from persistent state\n // added here for smoother transition, otherwise user will be logged out\n return state.userToken || state.token\n }\n }\n}\n\nexport default oauth\n","const PASSWORD_STRATEGY = 'password'\nconst TOKEN_STRATEGY = 'token'\n\n// MFA strategies\nconst TOTP_STRATEGY = 'totp'\nconst RECOVERY_STRATEGY = 'recovery'\n\n// initial state\nconst state = {\n app: null,\n settings: {},\n strategy: PASSWORD_STRATEGY,\n initStrategy: PASSWORD_STRATEGY // default strategy from config\n}\n\nconst resetState = (state) => {\n state.strategy = state.initStrategy\n state.settings = {}\n state.app = null\n}\n\n// getters\nconst getters = {\n app: (state, getters) => {\n return state.app\n },\n settings: (state, getters) => {\n return state.settings\n },\n requiredPassword: (state, getters, rootState) => {\n return state.strategy === PASSWORD_STRATEGY\n },\n requiredToken: (state, getters, rootState) => {\n return state.strategy === TOKEN_STRATEGY\n },\n requiredTOTP: (state, getters, rootState) => {\n return state.strategy === TOTP_STRATEGY\n },\n requiredRecovery: (state, getters, rootState) => {\n return state.strategy === RECOVERY_STRATEGY\n }\n}\n\n// mutations\nconst mutations = {\n setInitialStrategy (state, strategy) {\n if (strategy) {\n state.initStrategy = strategy\n state.strategy = strategy\n }\n },\n requirePassword (state) {\n state.strategy = PASSWORD_STRATEGY\n },\n requireToken (state) {\n state.strategy = TOKEN_STRATEGY\n },\n requireMFA (state, { app, settings }) {\n state.settings = settings\n state.app = app\n state.strategy = TOTP_STRATEGY // default strategy of MFA\n },\n requireRecovery (state) {\n state.strategy = RECOVERY_STRATEGY\n },\n requireTOTP (state) {\n state.strategy = TOTP_STRATEGY\n },\n abortMFA (state) {\n resetState(state)\n }\n}\n\n// actions\nconst actions = {\n // eslint-disable-next-line camelcase\n async login ({ state, dispatch, commit }, { access_token }) {\n commit('setToken', access_token, { root: true })\n await dispatch('loginUser', access_token, { root: true })\n resetState(state)\n }\n}\n\nexport default {\n namespaced: true,\n state,\n getters,\n mutations,\n actions\n}\n","import fileTypeService from '../services/file_type/file_type.service.js'\n\nconst mediaViewer = {\n state: {\n media: [],\n currentIndex: 0,\n activated: false\n },\n mutations: {\n setMedia (state, media) {\n state.media = media\n },\n setCurrent (state, index) {\n state.activated = true\n state.currentIndex = index\n },\n close (state) {\n state.activated = false\n }\n },\n actions: {\n setMedia ({ commit }, attachments) {\n const media = attachments.filter(attachment => {\n const type = fileTypeService.fileType(attachment.mimetype)\n return type === 'image' || type === 'video'\n })\n commit('setMedia', media)\n },\n setCurrent ({ commit, state }, current) {\n const index = state.media.indexOf(current)\n commit('setCurrent', index || 0)\n },\n closeMediaViewer ({ commit }) {\n commit('close')\n }\n }\n}\n\nexport default mediaViewer\n","const oauthTokens = {\n state: {\n tokens: []\n },\n actions: {\n fetchTokens ({ rootState, commit }) {\n rootState.api.backendInteractor.fetchOAuthTokens().then((tokens) => {\n commit('swapTokens', tokens)\n })\n },\n revokeToken ({ rootState, commit, state }, id) {\n rootState.api.backendInteractor.revokeOAuthToken(id).then((response) => {\n if (response.status === 201) {\n commit('swapTokens', state.tokens.filter(token => token.id !== id))\n }\n })\n }\n },\n mutations: {\n swapTokens (state, tokens) {\n state.tokens = tokens\n }\n }\n}\n\nexport default oauthTokens\n","import filter from 'lodash/filter'\n\nconst reports = {\n state: {\n userId: null,\n statuses: [],\n modalActivated: false\n },\n mutations: {\n openUserReportingModal (state, { userId, statuses }) {\n state.userId = userId\n state.statuses = statuses\n state.modalActivated = true\n },\n closeUserReportingModal (state) {\n state.modalActivated = false\n }\n },\n actions: {\n openUserReportingModal ({ rootState, commit }, userId) {\n const statuses = filter(rootState.statuses.allStatuses, status => status.user.id === userId)\n commit('openUserReportingModal', { userId, statuses })\n },\n closeUserReportingModal ({ commit }) {\n commit('closeUserReportingModal')\n }\n }\n}\n\nexport default reports\n","import { merge } from 'lodash'\nimport { set } from 'vue'\n\nconst polls = {\n state: {\n // Contains key = id, value = number of trackers for this poll\n trackedPolls: {},\n pollsObject: {}\n },\n mutations: {\n mergeOrAddPoll (state, poll) {\n const existingPoll = state.pollsObject[poll.id]\n // Make expired-state change trigger re-renders properly\n poll.expired = Date.now() > Date.parse(poll.expires_at)\n if (existingPoll) {\n set(state.pollsObject, poll.id, merge(existingPoll, poll))\n } else {\n set(state.pollsObject, poll.id, poll)\n }\n },\n trackPoll (state, pollId) {\n const currentValue = state.trackedPolls[pollId]\n if (currentValue) {\n set(state.trackedPolls, pollId, currentValue + 1)\n } else {\n set(state.trackedPolls, pollId, 1)\n }\n },\n untrackPoll (state, pollId) {\n const currentValue = state.trackedPolls[pollId]\n if (currentValue) {\n set(state.trackedPolls, pollId, currentValue - 1)\n } else {\n set(state.trackedPolls, pollId, 0)\n }\n }\n },\n actions: {\n mergeOrAddPoll ({ commit }, poll) {\n commit('mergeOrAddPoll', poll)\n },\n updateTrackedPoll ({ rootState, dispatch, commit }, pollId) {\n rootState.api.backendInteractor.fetchPoll(pollId).then(poll => {\n setTimeout(() => {\n if (rootState.polls.trackedPolls[pollId]) {\n dispatch('updateTrackedPoll', pollId)\n }\n }, 30 * 1000)\n commit('mergeOrAddPoll', poll)\n })\n },\n trackPoll ({ rootState, commit, dispatch }, pollId) {\n if (!rootState.polls.trackedPolls[pollId]) {\n setTimeout(() => dispatch('updateTrackedPoll', pollId), 30 * 1000)\n }\n commit('trackPoll', pollId)\n },\n untrackPoll ({ commit }, pollId) {\n commit('untrackPoll', pollId)\n },\n votePoll ({ rootState, commit }, { id, pollId, choices }) {\n return rootState.api.backendInteractor.vote(pollId, choices).then(poll => {\n commit('mergeOrAddPoll', poll)\n return poll\n })\n }\n }\n}\n\nexport default polls\n","const postStatus = {\n state: {\n params: null,\n modalActivated: false\n },\n mutations: {\n openPostStatusModal (state, params) {\n state.params = params\n state.modalActivated = true\n },\n closePostStatusModal (state) {\n state.modalActivated = false\n }\n },\n actions: {\n openPostStatusModal ({ commit }, params) {\n commit('openPostStatusModal', params)\n },\n closePostStatusModal ({ commit }) {\n commit('closePostStatusModal')\n }\n }\n}\n\nexport default postStatus\n","import merge from 'lodash.merge'\nimport objectPath from 'object-path'\nimport localforage from 'localforage'\nimport { each } from 'lodash'\n\nlet loaded = false\n\nconst defaultReducer = (state, paths) => (\n paths.length === 0 ? state : paths.reduce((substate, path) => {\n objectPath.set(substate, path, objectPath.get(state, path))\n return substate\n }, {})\n)\n\nconst saveImmedeatelyActions = [\n 'markNotificationsAsSeen',\n 'clearCurrentUser',\n 'setCurrentUser',\n 'setHighlight',\n 'setOption',\n 'setClientData',\n 'setToken',\n 'clearToken'\n]\n\nconst defaultStorage = (() => {\n return localforage\n})()\n\nexport default function createPersistedState ({\n key = 'vuex-lz',\n paths = [],\n getState = (key, storage) => {\n let value = storage.getItem(key)\n return value\n },\n setState = (key, state, storage) => {\n if (!loaded) {\n console.log('waiting for old state to be loaded...')\n return Promise.resolve()\n } else {\n return storage.setItem(key, state)\n }\n },\n reducer = defaultReducer,\n storage = defaultStorage,\n subscriber = store => handler => store.subscribe(handler)\n} = {}) {\n return getState(key, storage).then((savedState) => {\n return store => {\n try {\n if (savedState !== null && typeof savedState === 'object') {\n // build user cache\n const usersState = savedState.users || {}\n usersState.usersObject = {}\n const users = usersState.users || []\n each(users, (user) => { usersState.usersObject[user.id] = user })\n savedState.users = usersState\n\n store.replaceState(\n merge({}, store.state, savedState)\n )\n }\n loaded = true\n } catch (e) {\n console.log(\"Couldn't load state\")\n console.error(e)\n loaded = true\n }\n subscriber(store)((mutation, state) => {\n try {\n if (saveImmedeatelyActions.includes(mutation.type)) {\n setState(key, reducer(state, paths), storage)\n .then(success => {\n if (typeof success !== 'undefined') {\n if (mutation.type === 'setOption' || mutation.type === 'setCurrentUser') {\n store.dispatch('settingsSaved', { success })\n }\n }\n }, error => {\n if (mutation.type === 'setOption' || mutation.type === 'setCurrentUser') {\n store.dispatch('settingsSaved', { error })\n }\n })\n }\n } catch (e) {\n console.log(\"Couldn't persist state:\")\n console.log(e)\n }\n })\n }\n })\n}\n","export default (store) => {\n store.subscribe((mutation, state) => {\n const vapidPublicKey = state.instance.vapidPublicKey\n const webPushNotification = state.config.webPushNotifications\n const permission = state.interface.notificationPermission === 'granted'\n const user = state.users.currentUser\n\n const isUserMutation = mutation.type === 'setCurrentUser'\n const isVapidMutation = mutation.type === 'setInstanceOption' && mutation.payload.name === 'vapidPublicKey'\n const isPermMutation = mutation.type === 'setNotificationPermission' && mutation.payload === 'granted'\n const isUserConfigMutation = mutation.type === 'setOption' && mutation.payload.name === 'webPushNotifications'\n const isVisibilityMutation = mutation.type === 'setOption' && mutation.payload.name === 'notificationVisibility'\n\n if (isUserMutation || isVapidMutation || isPermMutation || isUserConfigMutation || isVisibilityMutation) {\n if (user && vapidPublicKey && permission && webPushNotification) {\n return store.dispatch('registerPushNotifications')\n } else if (isUserConfigMutation && !webPushNotification) {\n return store.dispatch('unregisterPushNotifications')\n }\n }\n })\n}\n","import * as bodyScrollLock from 'body-scroll-lock'\n\nlet previousNavPaddingRight\nlet previousAppBgWrapperRight\n\nconst disableBodyScroll = (el) => {\n const scrollBarGap = window.innerWidth - document.documentElement.clientWidth\n bodyScrollLock.disableBodyScroll(el, {\n reserveScrollBarGap: true\n })\n setTimeout(() => {\n // If previousNavPaddingRight is already set, don't set it again.\n if (previousNavPaddingRight === undefined) {\n const navEl = document.getElementById('nav')\n previousNavPaddingRight = window.getComputedStyle(navEl).getPropertyValue('padding-right')\n navEl.style.paddingRight = previousNavPaddingRight ? `calc(${previousNavPaddingRight} + ${scrollBarGap}px)` : `${scrollBarGap}px`\n }\n // If previousAppBgWrapeprRight is already set, don't set it again.\n if (previousAppBgWrapperRight === undefined) {\n const appBgWrapperEl = document.getElementById('app_bg_wrapper')\n previousAppBgWrapperRight = window.getComputedStyle(appBgWrapperEl).getPropertyValue('right')\n appBgWrapperEl.style.right = previousAppBgWrapperRight ? `calc(${previousAppBgWrapperRight} + ${scrollBarGap}px)` : `${scrollBarGap}px`\n }\n document.body.classList.add('scroll-locked')\n })\n}\n\nconst enableBodyScroll = (el) => {\n setTimeout(() => {\n if (previousNavPaddingRight !== undefined) {\n document.getElementById('nav').style.paddingRight = previousNavPaddingRight\n // Restore previousNavPaddingRight to undefined so disableBodyScroll knows it can be set again.\n previousNavPaddingRight = undefined\n }\n if (previousAppBgWrapperRight !== undefined) {\n document.getElementById('app_bg_wrapper').style.right = previousAppBgWrapperRight\n // Restore previousAppBgWrapperRight to undefined so disableBodyScroll knows it can be set again.\n previousAppBgWrapperRight = undefined\n }\n document.body.classList.remove('scroll-locked')\n })\n bodyScrollLock.enableBodyScroll(el)\n}\n\nconst directive = {\n inserted: (el, binding) => {\n if (binding.value) {\n disableBodyScroll(el)\n }\n },\n componentUpdated: (el, binding) => {\n if (binding.oldValue === binding.value) {\n return\n }\n\n if (binding.value) {\n disableBodyScroll(el)\n } else {\n enableBodyScroll(el)\n }\n },\n unbind: (el) => {\n enableBodyScroll(el)\n }\n}\n\nexport default (Vue) => {\n Vue.directive('body-scroll-lock', directive)\n}\n","import Vue from 'vue'\nimport VueRouter from 'vue-router'\nimport routes from './routes'\nimport App from '../App.vue'\nimport { windowWidth } from '../services/window_utils/window_utils'\nimport { getOrCreateApp, getClientToken } from '../services/new_api/oauth.js'\nimport backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js'\n\nconst getStatusnetConfig = async ({ store }) => {\n try {\n const res = await window.fetch('/api/statusnet/config.json')\n if (res.ok) {\n const data = await res.json()\n const { name, closed: registrationClosed, textlimit, uploadlimit, server, vapidPublicKey, safeDMMentionsEnabled } = data.site\n\n store.dispatch('setInstanceOption', { name: 'name', value: name })\n store.dispatch('setInstanceOption', { name: 'registrationOpen', value: (registrationClosed === '0') })\n store.dispatch('setInstanceOption', { name: 'textlimit', value: parseInt(textlimit) })\n store.dispatch('setInstanceOption', { name: 'server', value: server })\n store.dispatch('setInstanceOption', { name: 'safeDM', value: safeDMMentionsEnabled !== '0' })\n\n // TODO: default values for this stuff, added if to not make it break on\n // my dev config out of the box.\n if (uploadlimit) {\n store.dispatch('setInstanceOption', { name: 'uploadlimit', value: parseInt(uploadlimit.uploadlimit) })\n store.dispatch('setInstanceOption', { name: 'avatarlimit', value: parseInt(uploadlimit.avatarlimit) })\n store.dispatch('setInstanceOption', { name: 'backgroundlimit', value: parseInt(uploadlimit.backgroundlimit) })\n store.dispatch('setInstanceOption', { name: 'bannerlimit', value: parseInt(uploadlimit.bannerlimit) })\n }\n\n if (vapidPublicKey) {\n store.dispatch('setInstanceOption', { name: 'vapidPublicKey', value: vapidPublicKey })\n }\n\n return data.site.pleromafe\n } else {\n throw (res)\n }\n } catch (error) {\n console.error('Could not load statusnet config, potentially fatal')\n console.error(error)\n }\n}\n\nconst getStaticConfig = async () => {\n try {\n const res = await window.fetch('/static/config.json')\n if (res.ok) {\n return res.json()\n } else {\n throw (res)\n }\n } catch (error) {\n console.warn('Failed to load static/config.json, continuing without it.')\n console.warn(error)\n return {}\n }\n}\n\nconst setSettings = async ({ apiConfig, staticConfig, store }) => {\n const overrides = window.___pleromafe_dev_overrides || {}\n const env = window.___pleromafe_mode.NODE_ENV\n\n // This takes static config and overrides properties that are present in apiConfig\n let config = {}\n if (overrides.staticConfigPreference && env === 'development') {\n console.warn('OVERRIDING API CONFIG WITH STATIC CONFIG')\n config = Object.assign({}, apiConfig, staticConfig)\n } else {\n config = Object.assign({}, staticConfig, apiConfig)\n }\n\n const copyInstanceOption = (name) => {\n store.dispatch('setInstanceOption', { name, value: config[name] })\n }\n\n copyInstanceOption('nsfwCensorImage')\n copyInstanceOption('background')\n copyInstanceOption('hidePostStats')\n copyInstanceOption('hideUserStats')\n copyInstanceOption('hideFilteredStatuses')\n copyInstanceOption('logo')\n\n store.dispatch('setInstanceOption', {\n name: 'logoMask',\n value: typeof config.logoMask === 'undefined'\n ? true\n : config.logoMask\n })\n\n store.dispatch('setInstanceOption', {\n name: 'logoMargin',\n value: typeof config.logoMargin === 'undefined'\n ? 0\n : config.logoMargin\n })\n store.commit('authFlow/setInitialStrategy', config.loginMethod)\n\n copyInstanceOption('redirectRootNoLogin')\n copyInstanceOption('redirectRootLogin')\n copyInstanceOption('showInstanceSpecificPanel')\n copyInstanceOption('minimalScopesMode')\n copyInstanceOption('hideMutedPosts')\n copyInstanceOption('collapseMessageWithSubject')\n copyInstanceOption('scopeCopy')\n copyInstanceOption('subjectLineBehavior')\n copyInstanceOption('postContentType')\n copyInstanceOption('alwaysShowSubjectInput')\n copyInstanceOption('noAttachmentLinks')\n copyInstanceOption('showFeaturesPanel')\n\n return store.dispatch('setTheme', config['theme'])\n}\n\nconst getTOS = async ({ store }) => {\n try {\n const res = await window.fetch('/static/terms-of-service.html')\n if (res.ok) {\n const html = await res.text()\n store.dispatch('setInstanceOption', { name: 'tos', value: html })\n } else {\n throw (res)\n }\n } catch (e) {\n console.warn(\"Can't load TOS\")\n console.warn(e)\n }\n}\n\nconst getInstancePanel = async ({ store }) => {\n try {\n const res = await window.fetch('/instance/panel.html')\n if (res.ok) {\n const html = await res.text()\n store.dispatch('setInstanceOption', { name: 'instanceSpecificPanelContent', value: html })\n } else {\n throw (res)\n }\n } catch (e) {\n console.warn(\"Can't load instance panel\")\n console.warn(e)\n }\n}\n\nconst getStickers = async ({ store }) => {\n try {\n const res = await window.fetch('/static/stickers.json')\n if (res.ok) {\n const values = await res.json()\n const stickers = (await Promise.all(\n Object.entries(values).map(async ([name, path]) => {\n const resPack = await window.fetch(path + 'pack.json')\n var meta = {}\n if (resPack.ok) {\n meta = await resPack.json()\n }\n return {\n pack: name,\n path,\n meta\n }\n })\n )).sort((a, b) => {\n return a.meta.title.localeCompare(b.meta.title)\n })\n store.dispatch('setInstanceOption', { name: 'stickers', value: stickers })\n } else {\n throw (res)\n }\n } catch (e) {\n console.warn(\"Can't load stickers\")\n console.warn(e)\n }\n}\n\nconst getStaticEmoji = async ({ store }) => {\n try {\n const res = await window.fetch('/static/emoji.json')\n if (res.ok) {\n const values = await res.json()\n const emoji = Object.keys(values).map((key) => {\n return {\n displayText: key,\n imageUrl: false,\n replacement: values[key]\n }\n }).sort((a, b) => a.displayText - b.displayText)\n store.dispatch('setInstanceOption', { name: 'emoji', value: emoji })\n } else {\n throw (res)\n }\n } catch (e) {\n console.warn(\"Can't load static emoji\")\n console.warn(e)\n }\n}\n\n// This is also used to indicate if we have a 'pleroma backend' or not.\n// Somewhat weird, should probably be somewhere else.\nconst getCustomEmoji = async ({ store }) => {\n try {\n const res = await window.fetch('/api/pleroma/emoji.json')\n if (res.ok) {\n const result = await res.json()\n const values = Array.isArray(result) ? Object.assign({}, ...result) : result\n const emoji = Object.entries(values).map(([key, value]) => {\n const imageUrl = value.image_url\n return {\n displayText: key,\n imageUrl: imageUrl ? store.state.instance.server + imageUrl : value,\n tags: imageUrl ? value.tags.sort((a, b) => a > b ? 1 : 0) : ['utf'],\n replacement: `:${key}: `\n }\n // Technically could use tags but those are kinda useless right now, should have been \"pack\" field, that would be more useful\n }).sort((a, b) => a.displayText.toLowerCase() > b.displayText.toLowerCase() ? 1 : 0)\n store.dispatch('setInstanceOption', { name: 'customEmoji', value: emoji })\n store.dispatch('setInstanceOption', { name: 'pleromaBackend', value: true })\n } else {\n throw (res)\n }\n } catch (e) {\n store.dispatch('setInstanceOption', { name: 'pleromaBackend', value: false })\n console.warn(\"Can't load custom emojis, maybe not a Pleroma instance?\")\n console.warn(e)\n }\n}\n\nconst getAppSecret = async ({ store }) => {\n const { state, commit } = store\n const { oauth, instance } = state\n return getOrCreateApp({ ...oauth, instance: instance.server, commit })\n .then((app) => getClientToken({ ...app, instance: instance.server }))\n .then((token) => {\n commit('setAppToken', token.access_token)\n commit('setBackendInteractor', backendInteractorService(store.getters.getToken()))\n })\n}\n\nconst getNodeInfo = async ({ store }) => {\n try {\n const res = await window.fetch('/nodeinfo/2.0.json')\n if (res.ok) {\n const data = await res.json()\n const metadata = data.metadata\n const features = metadata.features\n store.dispatch('setInstanceOption', { name: 'mediaProxyAvailable', value: features.includes('media_proxy') })\n store.dispatch('setInstanceOption', { name: 'chatAvailable', value: features.includes('chat') })\n store.dispatch('setInstanceOption', { name: 'gopherAvailable', value: features.includes('gopher') })\n store.dispatch('setInstanceOption', { name: 'pollsAvailable', value: features.includes('polls') })\n store.dispatch('setInstanceOption', { name: 'pollLimits', value: metadata.pollLimits })\n store.dispatch('setInstanceOption', { name: 'mailerEnabled', value: metadata.mailerEnabled })\n\n store.dispatch('setInstanceOption', { name: 'restrictedNicknames', value: metadata.restrictedNicknames })\n store.dispatch('setInstanceOption', { name: 'postFormats', value: metadata.postFormats })\n\n const suggestions = metadata.suggestions\n store.dispatch('setInstanceOption', { name: 'suggestionsEnabled', value: suggestions.enabled })\n store.dispatch('setInstanceOption', { name: 'suggestionsWeb', value: suggestions.web })\n\n const software = data.software\n store.dispatch('setInstanceOption', { name: 'backendVersion', value: software.version })\n\n const frontendVersion = window.___pleromafe_commit_hash\n store.dispatch('setInstanceOption', { name: 'frontendVersion', value: frontendVersion })\n store.dispatch('setInstanceOption', { name: 'tagPolicyAvailable', value: metadata.federation.mrf_policies.includes('TagPolicy') })\n } else {\n throw (res)\n }\n } catch (e) {\n console.warn('Could not load nodeinfo')\n console.warn(e)\n }\n}\n\nconst setConfig = async ({ store }) => {\n // apiConfig, staticConfig\n const configInfos = await Promise.all([getStatusnetConfig({ store }), getStaticConfig()])\n const apiConfig = configInfos[0]\n const staticConfig = configInfos[1]\n\n await setSettings({ store, apiConfig, staticConfig }).then(getAppSecret({ store }))\n}\n\nconst checkOAuthToken = async ({ store }) => {\n return new Promise(async (resolve, reject) => {\n if (store.getters.getUserToken()) {\n try {\n await store.dispatch('loginUser', store.getters.getUserToken())\n } catch (e) {\n console.log(e)\n }\n }\n resolve()\n })\n}\n\nconst afterStoreSetup = async ({ store, i18n }) => {\n if (store.state.config.customTheme) {\n // This is a hack to deal with async loading of config.json and themes\n // See: style_setter.js, setPreset()\n window.themeLoaded = true\n store.dispatch('setOption', {\n name: 'customTheme',\n value: store.state.config.customTheme\n })\n }\n\n const width = windowWidth()\n store.dispatch('setMobileLayout', width <= 800)\n\n // Now we can try getting the server settings and logging in\n await Promise.all([\n checkOAuthToken({ store }),\n setConfig({ store }),\n getTOS({ store }),\n getInstancePanel({ store }),\n getStickers({ store }),\n getStaticEmoji({ store }),\n getCustomEmoji({ store }),\n getNodeInfo({ store })\n ])\n\n const router = new VueRouter({\n mode: 'history',\n routes: routes(store),\n scrollBehavior: (to, _from, savedPosition) => {\n if (to.matched.some(m => m.meta.dontScroll)) {\n return false\n }\n return savedPosition || { x: 0, y: 0 }\n }\n })\n\n /* eslint-disable no-new */\n return new Vue({\n router,\n store,\n i18n,\n el: '#app',\n render: h => h(App)\n })\n}\n\nexport default afterStoreSetup\n","import PublicTimeline from 'components/public_timeline/public_timeline.vue'\nimport PublicAndExternalTimeline from 'components/public_and_external_timeline/public_and_external_timeline.vue'\nimport FriendsTimeline from 'components/friends_timeline/friends_timeline.vue'\nimport TagTimeline from 'components/tag_timeline/tag_timeline.vue'\nimport ConversationPage from 'components/conversation-page/conversation-page.vue'\nimport Interactions from 'components/interactions/interactions.vue'\nimport DMs from 'components/dm_timeline/dm_timeline.vue'\nimport UserProfile from 'components/user_profile/user_profile.vue'\nimport Search from 'components/search/search.vue'\nimport Settings from 'components/settings/settings.vue'\nimport Registration from 'components/registration/registration.vue'\nimport PasswordReset from 'components/password_reset/password_reset.vue'\nimport UserSettings from 'components/user_settings/user_settings.vue'\nimport FollowRequests from 'components/follow_requests/follow_requests.vue'\nimport OAuthCallback from 'components/oauth_callback/oauth_callback.vue'\nimport Notifications from 'components/notifications/notifications.vue'\nimport AuthForm from 'components/auth_form/auth_form.js'\nimport ChatPanel from 'components/chat_panel/chat_panel.vue'\nimport WhoToFollow from 'components/who_to_follow/who_to_follow.vue'\nimport About from 'components/about/about.vue'\n\nexport default (store) => {\n const validateAuthenticatedRoute = (to, from, next) => {\n if (store.state.users.currentUser) {\n next()\n } else {\n next(store.state.instance.redirectRootNoLogin || '/main/all')\n }\n }\n\n return [\n { name: 'root',\n path: '/',\n redirect: _to => {\n return (store.state.users.currentUser\n ? store.state.instance.redirectRootLogin\n : store.state.instance.redirectRootNoLogin) || '/main/all'\n }\n },\n { name: 'public-external-timeline', path: '/main/all', component: PublicAndExternalTimeline },\n { name: 'public-timeline', path: '/main/public', component: PublicTimeline },\n { name: 'friends', path: '/main/friends', component: FriendsTimeline, beforeEnter: validateAuthenticatedRoute },\n { name: 'tag-timeline', path: '/tag/:tag', component: TagTimeline },\n { name: 'conversation', path: '/notice/:id', component: ConversationPage, meta: { dontScroll: true } },\n { name: 'external-user-profile', path: '/users/:id', component: UserProfile },\n { name: 'interactions', path: '/users/:username/interactions', component: Interactions, beforeEnter: validateAuthenticatedRoute },\n { name: 'dms', path: '/users/:username/dms', component: DMs, beforeEnter: validateAuthenticatedRoute },\n { name: 'settings', path: '/settings', component: Settings },\n { name: 'registration', path: '/registration', component: Registration },\n { name: 'password-reset', path: '/password-reset', component: PasswordReset },\n { name: 'registration-token', path: '/registration/:token', component: Registration },\n { name: 'friend-requests', path: '/friend-requests', component: FollowRequests, beforeEnter: validateAuthenticatedRoute },\n { name: 'user-settings', path: '/user-settings', component: UserSettings, beforeEnter: validateAuthenticatedRoute },\n { name: 'notifications', path: '/:username/notifications', component: Notifications, beforeEnter: validateAuthenticatedRoute },\n { name: 'login', path: '/login', component: AuthForm },\n { name: 'chat', path: '/chat', component: ChatPanel, props: () => ({ floating: false }) },\n { name: 'oauth-callback', path: '/oauth-callback', component: OAuthCallback, props: (route) => ({ code: route.query.code }) },\n { name: 'search', path: '/search', component: Search, props: (route) => ({ query: route.query.query }) },\n { name: 'who-to-follow', path: '/who-to-follow', component: WhoToFollow, beforeEnter: validateAuthenticatedRoute },\n { name: 'about', path: '/about', component: About },\n { name: 'user-profile', path: '/(users/)?:name', component: UserProfile }\n ]\n}\n","/* script */\nexport * from \"!!babel-loader!./public_timeline.js\"\nimport __vue_script__ from \"!!babel-loader!./public_timeline.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-5f2a502e\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./public_timeline.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","// style-loader: Adds some css to the DOM by adding a \n","import FollowCard from '../follow_card/follow_card.vue'\nimport Conversation from '../conversation/conversation.vue'\nimport Status from '../status/status.vue'\nimport map from 'lodash/map'\n\nconst Search = {\n components: {\n FollowCard,\n Conversation,\n Status\n },\n props: [\n 'query'\n ],\n data () {\n return {\n loaded: false,\n loading: false,\n searchTerm: this.query || '',\n userIds: [],\n statuses: [],\n hashtags: [],\n currenResultTab: 'statuses'\n }\n },\n computed: {\n users () {\n return this.userIds.map(userId => this.$store.getters.findUser(userId))\n },\n visibleStatuses () {\n const allStatusesObject = this.$store.state.statuses.allStatusesObject\n\n return this.statuses.filter(status =>\n allStatusesObject[status.id] && !allStatusesObject[status.id].deleted\n )\n }\n },\n mounted () {\n this.search(this.query)\n },\n watch: {\n query (newValue) {\n this.searchTerm = newValue\n this.search(newValue)\n }\n },\n methods: {\n newQuery (query) {\n this.$router.push({ name: 'search', query: { query } })\n this.$refs.searchInput.focus()\n },\n search (query) {\n if (!query) {\n this.loading = false\n return\n }\n\n this.loading = true\n this.userIds = []\n this.statuses = []\n this.hashtags = []\n this.$refs.searchInput.blur()\n\n this.$store.dispatch('search', { q: query, resolve: true })\n .then(data => {\n this.loading = false\n this.userIds = map(data.accounts, 'id')\n this.statuses = data.statuses\n this.hashtags = data.hashtags\n this.currenResultTab = this.getActiveTab()\n this.loaded = true\n })\n },\n resultCount (tabName) {\n const length = this[tabName].length\n return length === 0 ? '' : ` (${length})`\n },\n onResultTabSwitch (key) {\n this.currenResultTab = key\n },\n getActiveTab () {\n if (this.visibleStatuses.length > 0) {\n return 'statuses'\n } else if (this.users.length > 0) {\n return 'people'\n } else if (this.hashtags.length > 0) {\n return 'hashtags'\n }\n\n return 'statuses'\n },\n lastHistoryRecord (hashtag) {\n return hashtag.history && hashtag.history[0]\n }\n }\n}\n\nexport default Search\n","/* eslint-env browser */\nimport { filter, trim } from 'lodash'\n\nimport TabSwitcher from '../tab_switcher/tab_switcher.js'\nimport StyleSwitcher from '../style_switcher/style_switcher.vue'\nimport InterfaceLanguageSwitcher from '../interface_language_switcher/interface_language_switcher.vue'\nimport { extractCommit } from '../../services/version/version.service'\n\nconst pleromaFeCommitUrl = 'https://git.pleroma.social/pleroma/pleroma-fe/commit/'\nconst pleromaBeCommitUrl = 'https://git.pleroma.social/pleroma/pleroma/commit/'\n\nconst settings = {\n data () {\n const user = this.$store.state.config\n const instance = this.$store.state.instance\n\n return {\n hideAttachmentsLocal: user.hideAttachments,\n hideAttachmentsInConvLocal: user.hideAttachmentsInConv,\n maxThumbnails: user.maxThumbnails,\n hideNsfwLocal: user.hideNsfw,\n useOneClickNsfw: user.useOneClickNsfw,\n hideISPLocal: user.hideISP,\n preloadImage: user.preloadImage,\n\n hidePostStatsLocal: typeof user.hidePostStats === 'undefined'\n ? instance.hidePostStats\n : user.hidePostStats,\n hidePostStatsDefault: this.$t('settings.values.' + instance.hidePostStats),\n\n hideUserStatsLocal: typeof user.hideUserStats === 'undefined'\n ? instance.hideUserStats\n : user.hideUserStats,\n hideUserStatsDefault: this.$t('settings.values.' + instance.hideUserStats),\n\n hideFilteredStatusesLocal: typeof user.hideFilteredStatuses === 'undefined'\n ? instance.hideFilteredStatuses\n : user.hideFilteredStatuses,\n hideFilteredStatusesDefault: this.$t('settings.values.' + instance.hideFilteredStatuses),\n\n notificationVisibilityLocal: user.notificationVisibility,\n replyVisibilityLocal: user.replyVisibility,\n loopVideoLocal: user.loopVideo,\n muteWordsString: user.muteWords.join('\\n'),\n autoLoadLocal: user.autoLoad,\n streamingLocal: user.streaming,\n pauseOnUnfocusedLocal: user.pauseOnUnfocused,\n hoverPreviewLocal: user.hoverPreview,\n autohideFloatingPostButtonLocal: user.autohideFloatingPostButton,\n\n hideMutedPostsLocal: typeof user.hideMutedPosts === 'undefined'\n ? instance.hideMutedPosts\n : user.hideMutedPosts,\n hideMutedPostsDefault: this.$t('settings.values.' + instance.hideMutedPosts),\n\n collapseMessageWithSubjectLocal: typeof user.collapseMessageWithSubject === 'undefined'\n ? instance.collapseMessageWithSubject\n : user.collapseMessageWithSubject,\n collapseMessageWithSubjectDefault: this.$t('settings.values.' + instance.collapseMessageWithSubject),\n\n subjectLineBehaviorLocal: typeof user.subjectLineBehavior === 'undefined'\n ? instance.subjectLineBehavior\n : user.subjectLineBehavior,\n subjectLineBehaviorDefault: instance.subjectLineBehavior,\n\n postContentTypeLocal: typeof user.postContentType === 'undefined'\n ? instance.postContentType\n : user.postContentType,\n postContentTypeDefault: instance.postContentType,\n\n alwaysShowSubjectInputLocal: typeof user.alwaysShowSubjectInput === 'undefined'\n ? instance.alwaysShowSubjectInput\n : user.alwaysShowSubjectInput,\n alwaysShowSubjectInputDefault: this.$t('settings.values.' + instance.alwaysShowSubjectInput),\n\n scopeCopyLocal: typeof user.scopeCopy === 'undefined'\n ? instance.scopeCopy\n : user.scopeCopy,\n scopeCopyDefault: this.$t('settings.values.' + instance.scopeCopy),\n\n minimalScopesModeLocal: typeof user.minimalScopesMode === 'undefined'\n ? instance.minimalScopesMode\n : user.minimalScopesMode,\n minimalScopesModeDefault: this.$t('settings.values.' + instance.minimalScopesMode),\n\n stopGifs: user.stopGifs,\n webPushNotificationsLocal: user.webPushNotifications,\n loopVideoSilentOnlyLocal: user.loopVideosSilentOnly,\n loopSilentAvailable:\n // Firefox\n Object.getOwnPropertyDescriptor(HTMLVideoElement.prototype, 'mozHasAudio') ||\n // Chrome-likes\n Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'webkitAudioDecodedByteCount') ||\n // Future spec, still not supported in Nightly 63 as of 08/2018\n Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'audioTracks'),\n playVideosInModal: user.playVideosInModal,\n useContainFit: user.useContainFit,\n\n backendVersion: instance.backendVersion,\n frontendVersion: instance.frontendVersion\n }\n },\n components: {\n TabSwitcher,\n StyleSwitcher,\n InterfaceLanguageSwitcher\n },\n computed: {\n user () {\n return this.$store.state.users.currentUser\n },\n currentSaveStateNotice () {\n return this.$store.state.interface.settings.currentSaveStateNotice\n },\n postFormats () {\n return this.$store.state.instance.postFormats || []\n },\n instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel },\n frontendVersionLink () {\n return pleromaFeCommitUrl + this.frontendVersion\n },\n backendVersionLink () {\n return pleromaBeCommitUrl + extractCommit(this.backendVersion)\n }\n },\n watch: {\n hideAttachmentsLocal (value) {\n this.$store.dispatch('setOption', { name: 'hideAttachments', value })\n },\n hideAttachmentsInConvLocal (value) {\n this.$store.dispatch('setOption', { name: 'hideAttachmentsInConv', value })\n },\n hidePostStatsLocal (value) {\n this.$store.dispatch('setOption', { name: 'hidePostStats', value })\n },\n hideUserStatsLocal (value) {\n this.$store.dispatch('setOption', { name: 'hideUserStats', value })\n },\n hideFilteredStatusesLocal (value) {\n this.$store.dispatch('setOption', { name: 'hideFilteredStatuses', value })\n },\n hideNsfwLocal (value) {\n this.$store.dispatch('setOption', { name: 'hideNsfw', value })\n },\n useOneClickNsfw (value) {\n this.$store.dispatch('setOption', { name: 'useOneClickNsfw', value })\n },\n preloadImage (value) {\n this.$store.dispatch('setOption', { name: 'preloadImage', value })\n },\n hideISPLocal (value) {\n this.$store.dispatch('setOption', { name: 'hideISP', value })\n },\n 'notificationVisibilityLocal.likes' (value) {\n this.$store.dispatch('setOption', { name: 'notificationVisibility', value: this.$store.state.config.notificationVisibility })\n },\n 'notificationVisibilityLocal.follows' (value) {\n this.$store.dispatch('setOption', { name: 'notificationVisibility', value: this.$store.state.config.notificationVisibility })\n },\n 'notificationVisibilityLocal.repeats' (value) {\n this.$store.dispatch('setOption', { name: 'notificationVisibility', value: this.$store.state.config.notificationVisibility })\n },\n 'notificationVisibilityLocal.mentions' (value) {\n this.$store.dispatch('setOption', { name: 'notificationVisibility', value: this.$store.state.config.notificationVisibility })\n },\n replyVisibilityLocal (value) {\n this.$store.dispatch('setOption', { name: 'replyVisibility', value })\n },\n loopVideoLocal (value) {\n this.$store.dispatch('setOption', { name: 'loopVideo', value })\n },\n loopVideoSilentOnlyLocal (value) {\n this.$store.dispatch('setOption', { name: 'loopVideoSilentOnly', value })\n },\n autoLoadLocal (value) {\n this.$store.dispatch('setOption', { name: 'autoLoad', value })\n },\n streamingLocal (value) {\n this.$store.dispatch('setOption', { name: 'streaming', value })\n },\n pauseOnUnfocusedLocal (value) {\n this.$store.dispatch('setOption', { name: 'pauseOnUnfocused', value })\n },\n hoverPreviewLocal (value) {\n this.$store.dispatch('setOption', { name: 'hoverPreview', value })\n },\n autohideFloatingPostButtonLocal (value) {\n this.$store.dispatch('setOption', { name: 'autohideFloatingPostButton', value })\n },\n muteWordsString (value) {\n value = filter(value.split('\\n'), (word) => trim(word).length > 0)\n this.$store.dispatch('setOption', { name: 'muteWords', value })\n },\n hideMutedPostsLocal (value) {\n this.$store.dispatch('setOption', { name: 'hideMutedPosts', value })\n },\n collapseMessageWithSubjectLocal (value) {\n this.$store.dispatch('setOption', { name: 'collapseMessageWithSubject', value })\n },\n scopeCopyLocal (value) {\n this.$store.dispatch('setOption', { name: 'scopeCopy', value })\n },\n alwaysShowSubjectInputLocal (value) {\n this.$store.dispatch('setOption', { name: 'alwaysShowSubjectInput', value })\n },\n subjectLineBehaviorLocal (value) {\n this.$store.dispatch('setOption', { name: 'subjectLineBehavior', value })\n },\n postContentTypeLocal (value) {\n this.$store.dispatch('setOption', { name: 'postContentType', value })\n },\n minimalScopesModeLocal (value) {\n this.$store.dispatch('setOption', { name: 'minimalScopesMode', value })\n },\n stopGifs (value) {\n this.$store.dispatch('setOption', { name: 'stopGifs', value })\n },\n webPushNotificationsLocal (value) {\n this.$store.dispatch('setOption', { name: 'webPushNotifications', value })\n if (value) this.$store.dispatch('registerPushNotifications')\n },\n playVideosInModal (value) {\n this.$store.dispatch('setOption', { name: 'playVideosInModal', value })\n },\n useContainFit (value) {\n this.$store.dispatch('setOption', { name: 'useContainFit', value })\n },\n maxThumbnails (value) {\n value = this.maxThumbnails = Math.floor(Math.max(value, 0))\n this.$store.dispatch('setOption', { name: 'maxThumbnails', value })\n }\n }\n}\n\nexport default settings\n","import { rgb2hex, hex2rgb, getContrastRatio, alphaBlend } from '../../services/color_convert/color_convert.js'\nimport { set, delete as del } from 'vue'\nimport { generateColors, generateShadows, generateRadii, generateFonts, composePreset, getThemes } from '../../services/style_setter/style_setter.js'\nimport ColorInput from '../color_input/color_input.vue'\nimport RangeInput from '../range_input/range_input.vue'\nimport OpacityInput from '../opacity_input/opacity_input.vue'\nimport ShadowControl from '../shadow_control/shadow_control.vue'\nimport FontControl from '../font_control/font_control.vue'\nimport ContrastRatio from '../contrast_ratio/contrast_ratio.vue'\nimport TabSwitcher from '../tab_switcher/tab_switcher.js'\nimport Preview from './preview.vue'\nimport ExportImport from '../export_import/export_import.vue'\n\n// List of color values used in v1\nconst v1OnlyNames = [\n 'bg',\n 'fg',\n 'text',\n 'link',\n 'cRed',\n 'cGreen',\n 'cBlue',\n 'cOrange'\n].map(_ => _ + 'ColorLocal')\n\nexport default {\n data () {\n return {\n availableStyles: [],\n selected: this.$store.state.config.theme,\n\n previewShadows: {},\n previewColors: {},\n previewRadii: {},\n previewFonts: {},\n\n shadowsInvalid: true,\n colorsInvalid: true,\n radiiInvalid: true,\n\n keepColor: false,\n keepShadows: false,\n keepOpacity: false,\n keepRoundness: false,\n keepFonts: false,\n\n textColorLocal: '',\n linkColorLocal: '',\n\n bgColorLocal: '',\n bgOpacityLocal: undefined,\n\n fgColorLocal: '',\n fgTextColorLocal: undefined,\n fgLinkColorLocal: undefined,\n\n btnColorLocal: undefined,\n btnTextColorLocal: undefined,\n btnOpacityLocal: undefined,\n\n inputColorLocal: undefined,\n inputTextColorLocal: undefined,\n inputOpacityLocal: undefined,\n\n panelColorLocal: undefined,\n panelTextColorLocal: undefined,\n panelLinkColorLocal: undefined,\n panelFaintColorLocal: undefined,\n panelOpacityLocal: undefined,\n\n topBarColorLocal: undefined,\n topBarTextColorLocal: undefined,\n topBarLinkColorLocal: undefined,\n\n alertErrorColorLocal: undefined,\n\n badgeOpacityLocal: undefined,\n badgeNotificationColorLocal: undefined,\n\n borderColorLocal: undefined,\n borderOpacityLocal: undefined,\n\n faintColorLocal: undefined,\n faintOpacityLocal: undefined,\n faintLinkColorLocal: undefined,\n\n cRedColorLocal: '',\n cBlueColorLocal: '',\n cGreenColorLocal: '',\n cOrangeColorLocal: '',\n\n shadowSelected: undefined,\n shadowsLocal: {},\n fontsLocal: {},\n\n btnRadiusLocal: '',\n inputRadiusLocal: '',\n checkboxRadiusLocal: '',\n panelRadiusLocal: '',\n avatarRadiusLocal: '',\n avatarAltRadiusLocal: '',\n attachmentRadiusLocal: '',\n tooltipRadiusLocal: ''\n }\n },\n created () {\n const self = this\n\n getThemes().then((themesComplete) => {\n self.availableStyles = themesComplete\n })\n },\n mounted () {\n this.normalizeLocalState(this.$store.state.config.customTheme)\n if (typeof this.shadowSelected === 'undefined') {\n this.shadowSelected = this.shadowsAvailable[0]\n }\n },\n computed: {\n selectedVersion () {\n return Array.isArray(this.selected) ? 1 : 2\n },\n currentColors () {\n return {\n bg: this.bgColorLocal,\n text: this.textColorLocal,\n link: this.linkColorLocal,\n\n fg: this.fgColorLocal,\n fgText: this.fgTextColorLocal,\n fgLink: this.fgLinkColorLocal,\n\n panel: this.panelColorLocal,\n panelText: this.panelTextColorLocal,\n panelLink: this.panelLinkColorLocal,\n panelFaint: this.panelFaintColorLocal,\n\n input: this.inputColorLocal,\n inputText: this.inputTextColorLocal,\n\n topBar: this.topBarColorLocal,\n topBarText: this.topBarTextColorLocal,\n topBarLink: this.topBarLinkColorLocal,\n\n btn: this.btnColorLocal,\n btnText: this.btnTextColorLocal,\n\n alertError: this.alertErrorColorLocal,\n badgeNotification: this.badgeNotificationColorLocal,\n\n faint: this.faintColorLocal,\n faintLink: this.faintLinkColorLocal,\n border: this.borderColorLocal,\n\n cRed: this.cRedColorLocal,\n cBlue: this.cBlueColorLocal,\n cGreen: this.cGreenColorLocal,\n cOrange: this.cOrangeColorLocal\n }\n },\n currentOpacity () {\n return {\n bg: this.bgOpacityLocal,\n btn: this.btnOpacityLocal,\n input: this.inputOpacityLocal,\n panel: this.panelOpacityLocal,\n topBar: this.topBarOpacityLocal,\n border: this.borderOpacityLocal,\n faint: this.faintOpacityLocal\n }\n },\n currentRadii () {\n return {\n btn: this.btnRadiusLocal,\n input: this.inputRadiusLocal,\n checkbox: this.checkboxRadiusLocal,\n panel: this.panelRadiusLocal,\n avatar: this.avatarRadiusLocal,\n avatarAlt: this.avatarAltRadiusLocal,\n tooltip: this.tooltipRadiusLocal,\n attachment: this.attachmentRadiusLocal\n }\n },\n preview () {\n return composePreset(this.previewColors, this.previewRadii, this.previewShadows, this.previewFonts)\n },\n previewTheme () {\n if (!this.preview.theme.colors) return { colors: {}, opacity: {}, radii: {}, shadows: {}, fonts: {} }\n return this.preview.theme\n },\n // This needs optimization maybe\n previewContrast () {\n if (!this.previewTheme.colors.bg) return {}\n const colors = this.previewTheme.colors\n const opacity = this.previewTheme.opacity\n if (!colors.bg) return {}\n const hints = (ratio) => ({\n text: ratio.toPrecision(3) + ':1',\n // AA level, AAA level\n aa: ratio >= 4.5,\n aaa: ratio >= 7,\n // same but for 18pt+ texts\n laa: ratio >= 3,\n laaa: ratio >= 4.5\n })\n\n // fgsfds :DDDD\n const fgs = {\n text: hex2rgb(colors.text),\n panelText: hex2rgb(colors.panelText),\n panelLink: hex2rgb(colors.panelLink),\n btnText: hex2rgb(colors.btnText),\n topBarText: hex2rgb(colors.topBarText),\n inputText: hex2rgb(colors.inputText),\n\n link: hex2rgb(colors.link),\n topBarLink: hex2rgb(colors.topBarLink),\n\n red: hex2rgb(colors.cRed),\n green: hex2rgb(colors.cGreen),\n blue: hex2rgb(colors.cBlue),\n orange: hex2rgb(colors.cOrange)\n }\n\n const bgs = {\n bg: hex2rgb(colors.bg),\n btn: hex2rgb(colors.btn),\n panel: hex2rgb(colors.panel),\n topBar: hex2rgb(colors.topBar),\n input: hex2rgb(colors.input),\n alertError: hex2rgb(colors.alertError),\n badgeNotification: hex2rgb(colors.badgeNotification)\n }\n\n /* This is a bit confusing because \"bottom layer\" used is text color\n * This is done to get worst case scenario when background below transparent\n * layer matches text color, making it harder to read the lower alpha is.\n */\n const ratios = {\n bgText: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.text), fgs.text),\n bgLink: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.link), fgs.link),\n bgRed: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.red), fgs.red),\n bgGreen: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.green), fgs.green),\n bgBlue: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.blue), fgs.blue),\n bgOrange: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.orange), fgs.orange),\n\n tintText: getContrastRatio(alphaBlend(bgs.bg, 0.5, fgs.panelText), fgs.text),\n\n panelText: getContrastRatio(alphaBlend(bgs.panel, opacity.panel, fgs.panelText), fgs.panelText),\n panelLink: getContrastRatio(alphaBlend(bgs.panel, opacity.panel, fgs.panelLink), fgs.panelLink),\n\n btnText: getContrastRatio(alphaBlend(bgs.btn, opacity.btn, fgs.btnText), fgs.btnText),\n\n inputText: getContrastRatio(alphaBlend(bgs.input, opacity.input, fgs.inputText), fgs.inputText),\n\n topBarText: getContrastRatio(alphaBlend(bgs.topBar, opacity.topBar, fgs.topBarText), fgs.topBarText),\n topBarLink: getContrastRatio(alphaBlend(bgs.topBar, opacity.topBar, fgs.topBarLink), fgs.topBarLink)\n }\n\n return Object.entries(ratios).reduce((acc, [k, v]) => { acc[k] = hints(v); return acc }, {})\n },\n previewRules () {\n if (!this.preview.rules) return ''\n return [\n ...Object.values(this.preview.rules),\n 'color: var(--text)',\n 'font-family: var(--interfaceFont, sans-serif)'\n ].join(';')\n },\n shadowsAvailable () {\n return Object.keys(this.previewTheme.shadows).sort()\n },\n currentShadowOverriden: {\n get () {\n return !!this.currentShadow\n },\n set (val) {\n if (val) {\n set(this.shadowsLocal, this.shadowSelected, this.currentShadowFallback.map(_ => Object.assign({}, _)))\n } else {\n del(this.shadowsLocal, this.shadowSelected)\n }\n }\n },\n currentShadowFallback () {\n return this.previewTheme.shadows[this.shadowSelected]\n },\n currentShadow: {\n get () {\n return this.shadowsLocal[this.shadowSelected]\n },\n set (v) {\n set(this.shadowsLocal, this.shadowSelected, v)\n }\n },\n themeValid () {\n return !this.shadowsInvalid && !this.colorsInvalid && !this.radiiInvalid\n },\n exportedTheme () {\n const saveEverything = (\n !this.keepFonts &&\n !this.keepShadows &&\n !this.keepOpacity &&\n !this.keepRoundness &&\n !this.keepColor\n )\n\n const theme = {}\n\n if (this.keepFonts || saveEverything) {\n theme.fonts = this.fontsLocal\n }\n if (this.keepShadows || saveEverything) {\n theme.shadows = this.shadowsLocal\n }\n if (this.keepOpacity || saveEverything) {\n theme.opacity = this.currentOpacity\n }\n if (this.keepColor || saveEverything) {\n theme.colors = this.currentColors\n }\n if (this.keepRoundness || saveEverything) {\n theme.radii = this.currentRadii\n }\n\n return {\n // To separate from other random JSON files and possible future theme formats\n _pleroma_theme_version: 2, theme\n }\n }\n },\n components: {\n ColorInput,\n OpacityInput,\n RangeInput,\n ContrastRatio,\n ShadowControl,\n FontControl,\n TabSwitcher,\n Preview,\n ExportImport\n },\n methods: {\n setCustomTheme () {\n this.$store.dispatch('setOption', {\n name: 'customTheme',\n value: {\n shadows: this.shadowsLocal,\n fonts: this.fontsLocal,\n opacity: this.currentOpacity,\n colors: this.currentColors,\n radii: this.currentRadii\n }\n })\n },\n onImport (parsed) {\n if (parsed._pleroma_theme_version === 1) {\n this.normalizeLocalState(parsed, 1)\n } else if (parsed._pleroma_theme_version === 2) {\n this.normalizeLocalState(parsed.theme, 2)\n }\n },\n importValidator (parsed) {\n const version = parsed._pleroma_theme_version\n return version >= 1 || version <= 2\n },\n clearAll () {\n const state = this.$store.state.config.customTheme\n const version = state.colors ? 2 : 'l1'\n this.normalizeLocalState(this.$store.state.config.customTheme, version)\n },\n\n // Clears all the extra stuff when loading V1 theme\n clearV1 () {\n Object.keys(this.$data)\n .filter(_ => _.endsWith('ColorLocal') || _.endsWith('OpacityLocal'))\n .filter(_ => !v1OnlyNames.includes(_))\n .forEach(key => {\n set(this.$data, key, undefined)\n })\n },\n\n clearRoundness () {\n Object.keys(this.$data)\n .filter(_ => _.endsWith('RadiusLocal'))\n .forEach(key => {\n set(this.$data, key, undefined)\n })\n },\n\n clearOpacity () {\n Object.keys(this.$data)\n .filter(_ => _.endsWith('OpacityLocal'))\n .forEach(key => {\n set(this.$data, key, undefined)\n })\n },\n\n clearShadows () {\n this.shadowsLocal = {}\n },\n\n clearFonts () {\n this.fontsLocal = {}\n },\n\n /**\n * This applies stored theme data onto form. Supports three versions of data:\n * v2 (version = 2) - newer version of themes.\n * v1 (version = 1) - older version of themes (import from file)\n * v1l (version = l1) - older version of theme (load from local storage)\n * v1 and v1l differ because of way themes were stored/exported.\n * @param {Object} input - input data\n * @param {Number} version - version of data. 0 means try to guess based on data. \"l1\" means v1, locastorage type\n */\n normalizeLocalState (input, version = 0) {\n const colors = input.colors || input\n const radii = input.radii || input\n const opacity = input.opacity\n const shadows = input.shadows || {}\n const fonts = input.fonts || {}\n\n if (version === 0) {\n if (input.version) version = input.version\n // Old v1 naming: fg is text, btn is foreground\n if (typeof colors.text === 'undefined' && typeof colors.fg !== 'undefined') {\n version = 1\n }\n // New v2 naming: text is text, fg is foreground\n if (typeof colors.text !== 'undefined' && typeof colors.fg !== 'undefined') {\n version = 2\n }\n }\n\n // Stuff that differs between V1 and V2\n if (version === 1) {\n this.fgColorLocal = rgb2hex(colors.btn)\n this.textColorLocal = rgb2hex(colors.fg)\n }\n\n if (!this.keepColor) {\n this.clearV1()\n const keys = new Set(version !== 1 ? Object.keys(colors) : [])\n if (version === 1 || version === 'l1') {\n keys\n .add('bg')\n .add('link')\n .add('cRed')\n .add('cBlue')\n .add('cGreen')\n .add('cOrange')\n }\n\n keys.forEach(key => {\n this[key + 'ColorLocal'] = rgb2hex(colors[key])\n })\n }\n\n if (!this.keepRoundness) {\n this.clearRoundness()\n Object.entries(radii).forEach(([k, v]) => {\n // 'Radius' is kept mostly for v1->v2 localstorage transition\n const key = k.endsWith('Radius') ? k.split('Radius')[0] : k\n this[key + 'RadiusLocal'] = v\n })\n }\n\n if (!this.keepShadows) {\n this.clearShadows()\n this.shadowsLocal = shadows\n this.shadowSelected = this.shadowsAvailable[0]\n }\n\n if (!this.keepFonts) {\n this.clearFonts()\n this.fontsLocal = fonts\n }\n\n if (opacity && !this.keepOpacity) {\n this.clearOpacity()\n Object.entries(opacity).forEach(([k, v]) => {\n if (typeof v === 'undefined' || v === null || Number.isNaN(v)) return\n this[k + 'OpacityLocal'] = v\n })\n }\n }\n },\n watch: {\n currentRadii () {\n try {\n this.previewRadii = generateRadii({ radii: this.currentRadii })\n this.radiiInvalid = false\n } catch (e) {\n this.radiiInvalid = true\n console.warn(e)\n }\n },\n shadowsLocal: {\n handler () {\n try {\n this.previewShadows = generateShadows({ shadows: this.shadowsLocal })\n this.shadowsInvalid = false\n } catch (e) {\n this.shadowsInvalid = true\n console.warn(e)\n }\n },\n deep: true\n },\n fontsLocal: {\n handler () {\n try {\n this.previewFonts = generateFonts({ fonts: this.fontsLocal })\n this.fontsInvalid = false\n } catch (e) {\n this.fontsInvalid = true\n console.warn(e)\n }\n },\n deep: true\n },\n currentColors () {\n try {\n this.previewColors = generateColors({\n opacity: this.currentOpacity,\n colors: this.currentColors\n })\n this.colorsInvalid = false\n } catch (e) {\n this.colorsInvalid = true\n console.warn(e)\n }\n },\n currentOpacity () {\n try {\n this.previewColors = generateColors({\n opacity: this.currentOpacity,\n colors: this.currentColors\n })\n } catch (e) {\n console.warn(e)\n }\n },\n selected () {\n if (this.selectedVersion === 1) {\n if (!this.keepRoundness) {\n this.clearRoundness()\n }\n\n if (!this.keepShadows) {\n this.clearShadows()\n }\n\n if (!this.keepOpacity) {\n this.clearOpacity()\n }\n\n if (!this.keepColor) {\n this.clearV1()\n\n this.bgColorLocal = this.selected[1]\n this.fgColorLocal = this.selected[2]\n this.textColorLocal = this.selected[3]\n this.linkColorLocal = this.selected[4]\n this.cRedColorLocal = this.selected[5]\n this.cGreenColorLocal = this.selected[6]\n this.cBlueColorLocal = this.selected[7]\n this.cOrangeColorLocal = this.selected[8]\n }\n } else if (this.selectedVersion >= 2) {\n this.normalizeLocalState(this.selected.theme, 2)\n }\n }\n }\n}\n","\n\n\n\n\n","\n\n\n","\n\n\n","import ColorInput from '../color_input/color_input.vue'\nimport OpacityInput from '../opacity_input/opacity_input.vue'\nimport { getCssShadow } from '../../services/style_setter/style_setter.js'\nimport { hex2rgb } from '../../services/color_convert/color_convert.js'\n\nexport default {\n // 'Value' and 'Fallback' can be undefined, but if they are\n // initially vue won't detect it when they become something else\n // therefore i'm using \"ready\" which should be passed as true when\n // data becomes available\n props: [\n 'value', 'fallback', 'ready'\n ],\n data () {\n return {\n selectedId: 0,\n // TODO there are some bugs regarding display of array (it's not getting updated when deleting for some reason)\n cValue: this.value || this.fallback || []\n }\n },\n components: {\n ColorInput,\n OpacityInput\n },\n methods: {\n add () {\n this.cValue.push(Object.assign({}, this.selected))\n this.selectedId = this.cValue.length - 1\n },\n del () {\n this.cValue.splice(this.selectedId, 1)\n this.selectedId = this.cValue.length === 0 ? undefined : this.selectedId - 1\n },\n moveUp () {\n const movable = this.cValue.splice(this.selectedId, 1)[0]\n this.cValue.splice(this.selectedId - 1, 0, movable)\n this.selectedId -= 1\n },\n moveDn () {\n const movable = this.cValue.splice(this.selectedId, 1)[0]\n this.cValue.splice(this.selectedId + 1, 0, movable)\n this.selectedId += 1\n }\n },\n beforeUpdate () {\n this.cValue = this.value || this.fallback\n },\n computed: {\n selected () {\n if (this.ready && this.cValue.length > 0) {\n return this.cValue[this.selectedId]\n } else {\n return {\n x: 0,\n y: 0,\n blur: 0,\n spread: 0,\n inset: false,\n color: '#000000',\n alpha: 1\n }\n }\n },\n moveUpValid () {\n return this.ready && this.selectedId > 0\n },\n moveDnValid () {\n return this.ready && this.selectedId < this.cValue.length - 1\n },\n present () {\n return this.ready &&\n typeof this.cValue[this.selectedId] !== 'undefined' &&\n !this.usingFallback\n },\n usingFallback () {\n return typeof this.value === 'undefined'\n },\n rgb () {\n return hex2rgb(this.selected.color)\n },\n style () {\n return this.ready ? {\n boxShadow: getCssShadow(this.cValue)\n } : {}\n }\n }\n}\n","import { set } from 'vue'\n\nexport default {\n props: [\n 'name', 'label', 'value', 'fallback', 'options', 'no-inherit'\n ],\n data () {\n return {\n lValue: this.value,\n availableOptions: [\n this.noInherit ? '' : 'inherit',\n 'custom',\n ...(this.options || []),\n 'serif',\n 'monospace',\n 'sans-serif'\n ].filter(_ => _)\n }\n },\n beforeUpdate () {\n this.lValue = this.value\n },\n computed: {\n present () {\n return typeof this.lValue !== 'undefined'\n },\n dValue () {\n return this.lValue || this.fallback || {}\n },\n family: {\n get () {\n return this.dValue.family\n },\n set (v) {\n set(this.lValue, 'family', v)\n this.$emit('input', this.lValue)\n }\n },\n isCustom () {\n return this.preset === 'custom'\n },\n preset: {\n get () {\n if (this.family === 'serif' ||\n this.family === 'sans-serif' ||\n this.family === 'monospace' ||\n this.family === 'inherit') {\n return this.family\n } else {\n return 'custom'\n }\n },\n set (v) {\n this.family = v === 'custom' ? '' : v\n }\n }\n }\n}\n","\n\n\n\n\n","\n\n\n\n\n","\n\n\n","import { validationMixin } from 'vuelidate'\nimport { required, sameAs } from 'vuelidate/lib/validators'\nimport { mapActions, mapState } from 'vuex'\n\nconst registration = {\n mixins: [validationMixin],\n data: () => ({\n user: {\n email: '',\n fullname: '',\n username: '',\n password: '',\n confirm: ''\n },\n captcha: {}\n }),\n validations: {\n user: {\n email: { required },\n username: { required },\n fullname: { required },\n password: { required },\n confirm: {\n required,\n sameAsPassword: sameAs('password')\n }\n }\n },\n created () {\n if ((!this.registrationOpen && !this.token) || this.signedIn) {\n this.$router.push({ name: 'root' })\n }\n\n this.setCaptcha()\n },\n computed: {\n token () { return this.$route.params.token },\n bioPlaceholder () {\n return this.$t('registration.bio_placeholder').replace(/\\s*\\n\\s*/g, ' \\n')\n },\n ...mapState({\n registrationOpen: (state) => state.instance.registrationOpen,\n signedIn: (state) => !!state.users.currentUser,\n isPending: (state) => state.users.signUpPending,\n serverValidationErrors: (state) => state.users.signUpErrors,\n termsOfService: (state) => state.instance.tos\n })\n },\n methods: {\n ...mapActions(['signUp', 'getCaptcha']),\n async submit () {\n this.user.nickname = this.user.username\n this.user.token = this.token\n\n this.user.captcha_solution = this.captcha.solution\n this.user.captcha_token = this.captcha.token\n this.user.captcha_answer_data = this.captcha.answer_data\n\n this.$v.$touch()\n\n if (!this.$v.$invalid) {\n try {\n await this.signUp(this.user)\n this.$router.push({ name: 'friends' })\n } catch (error) {\n console.warn('Registration failed: ' + error)\n }\n }\n },\n setCaptcha () {\n this.getCaptcha().then(cpt => { this.captcha = cpt })\n }\n }\n}\n\nexport default registration\n","import { mapState } from 'vuex'\nimport passwordResetApi from '../../services/new_api/password_reset.js'\n\nconst passwordReset = {\n data: () => ({\n user: {\n email: ''\n },\n isPending: false,\n success: false,\n throttled: false,\n error: null\n }),\n computed: {\n ...mapState({\n signedIn: (state) => !!state.users.currentUser,\n instance: state => state.instance\n }),\n mailerEnabled () {\n return this.instance.mailerEnabled\n }\n },\n created () {\n if (this.signedIn) {\n this.$router.push({ name: 'root' })\n }\n },\n methods: {\n dismissError () {\n this.error = null\n },\n submit () {\n this.isPending = true\n const email = this.user.email\n const instance = this.instance.server\n\n passwordResetApi({ instance, email }).then(({ status }) => {\n this.isPending = false\n this.user.email = ''\n\n if (status === 204) {\n this.success = true\n this.error = null\n } else if (status === 404 || status === 400) {\n this.error = this.$t('password_reset.not_found')\n this.$nextTick(() => {\n this.$refs.email.focus()\n })\n } else if (status === 429) {\n this.throttled = true\n this.error = this.$t('password_reset.too_many_requests')\n }\n }).catch(() => {\n this.isPending = false\n this.user.email = ''\n this.error = this.$t('general.generic_error')\n })\n }\n }\n}\n\nexport default passwordReset\n","import unescape from 'lodash/unescape'\nimport get from 'lodash/get'\nimport map from 'lodash/map'\nimport reject from 'lodash/reject'\nimport TabSwitcher from '../tab_switcher/tab_switcher.js'\nimport ImageCropper from '../image_cropper/image_cropper.vue'\nimport StyleSwitcher from '../style_switcher/style_switcher.vue'\nimport ScopeSelector from '../scope_selector/scope_selector.vue'\nimport fileSizeFormatService from '../../services/file_size_format/file_size_format.js'\nimport BlockCard from '../block_card/block_card.vue'\nimport MuteCard from '../mute_card/mute_card.vue'\nimport SelectableList from '../selectable_list/selectable_list.vue'\nimport ProgressButton from '../progress_button/progress_button.vue'\nimport EmojiInput from '../emoji-input/emoji-input.vue'\nimport suggestor from '../emoji-input/suggestor.js'\nimport Autosuggest from '../autosuggest/autosuggest.vue'\nimport Importer from '../importer/importer.vue'\nimport Exporter from '../exporter/exporter.vue'\nimport withSubscription from '../../hocs/with_subscription/with_subscription'\nimport Mfa from './mfa.vue'\n\nconst BlockList = withSubscription({\n fetch: (props, $store) => $store.dispatch('fetchBlocks'),\n select: (props, $store) => get($store.state.users.currentUser, 'blockIds', []),\n childPropName: 'items'\n})(SelectableList)\n\nconst MuteList = withSubscription({\n fetch: (props, $store) => $store.dispatch('fetchMutes'),\n select: (props, $store) => get($store.state.users.currentUser, 'muteIds', []),\n childPropName: 'items'\n})(SelectableList)\n\nconst UserSettings = {\n data () {\n return {\n newName: this.$store.state.users.currentUser.name,\n newBio: unescape(this.$store.state.users.currentUser.description),\n newLocked: this.$store.state.users.currentUser.locked,\n newNoRichText: this.$store.state.users.currentUser.no_rich_text,\n newDefaultScope: this.$store.state.users.currentUser.default_scope,\n hideFollows: this.$store.state.users.currentUser.hide_follows,\n hideFollowers: this.$store.state.users.currentUser.hide_followers,\n showRole: this.$store.state.users.currentUser.show_role,\n role: this.$store.state.users.currentUser.role,\n pickAvatarBtnVisible: true,\n bannerUploading: false,\n backgroundUploading: false,\n banner: null,\n bannerPreview: null,\n background: null,\n backgroundPreview: null,\n bannerUploadError: null,\n backgroundUploadError: null,\n deletingAccount: false,\n deleteAccountConfirmPasswordInput: '',\n deleteAccountError: false,\n changePasswordInputs: [ '', '', '' ],\n changedPassword: false,\n changePasswordError: false,\n activeTab: 'profile',\n notificationSettings: this.$store.state.users.currentUser.notification_settings\n }\n },\n created () {\n this.$store.dispatch('fetchTokens')\n },\n components: {\n StyleSwitcher,\n ScopeSelector,\n TabSwitcher,\n ImageCropper,\n BlockList,\n MuteList,\n EmojiInput,\n Autosuggest,\n BlockCard,\n MuteCard,\n ProgressButton,\n Importer,\n Exporter,\n Mfa\n },\n computed: {\n user () {\n return this.$store.state.users.currentUser\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({ emoji: [\n ...this.$store.state.instance.emoji,\n ...this.$store.state.instance.customEmoji\n ] })\n },\n pleromaBackend () {\n return this.$store.state.instance.pleromaBackend\n },\n minimalScopesMode () {\n return this.$store.state.instance.minimalScopesMode\n },\n vis () {\n return {\n public: { selected: this.newDefaultScope === 'public' },\n unlisted: { selected: this.newDefaultScope === 'unlisted' },\n private: { selected: this.newDefaultScope === 'private' },\n direct: { selected: this.newDefaultScope === 'direct' }\n }\n },\n currentSaveStateNotice () {\n return this.$store.state.interface.settings.currentSaveStateNotice\n },\n oauthTokens () {\n return this.$store.state.oauthTokens.tokens.map(oauthToken => {\n return {\n id: oauthToken.id,\n appName: oauthToken.app_name,\n validUntil: new Date(oauthToken.valid_until).toLocaleDateString()\n }\n })\n }\n },\n methods: {\n updateProfile () {\n this.$store.state.api.backendInteractor\n .updateProfile({\n params: {\n note: this.newBio,\n locked: this.newLocked,\n // Backend notation.\n /* eslint-disable camelcase */\n display_name: this.newName,\n default_scope: this.newDefaultScope,\n no_rich_text: this.newNoRichText,\n hide_follows: this.hideFollows,\n hide_followers: this.hideFollowers,\n show_role: this.showRole\n /* eslint-enable camelcase */\n } }).then((user) => {\n this.$store.commit('addNewUsers', [user])\n this.$store.commit('setCurrentUser', user)\n })\n },\n updateNotificationSettings () {\n this.$store.state.api.backendInteractor\n .updateNotificationSettings({ settings: this.notificationSettings })\n },\n changeVis (visibility) {\n this.newDefaultScope = visibility\n },\n uploadFile (slot, e) {\n const file = e.target.files[0]\n if (!file) { return }\n if (file.size > this.$store.state.instance[slot + 'limit']) {\n const filesize = fileSizeFormatService.fileSizeFormat(file.size)\n const allowedsize = fileSizeFormatService.fileSizeFormat(this.$store.state.instance[slot + 'limit'])\n this[slot + 'UploadError'] = this.$t('upload.error.base') + ' ' + this.$t('upload.error.file_too_big', { filesize: filesize.num, filesizeunit: filesize.unit, allowedsize: allowedsize.num, allowedsizeunit: allowedsize.unit })\n return\n }\n // eslint-disable-next-line no-undef\n const reader = new FileReader()\n reader.onload = ({ target }) => {\n const img = target.result\n this[slot + 'Preview'] = img\n this[slot] = file\n }\n reader.readAsDataURL(file)\n },\n submitAvatar (cropper, file) {\n const that = this\n return new Promise((resolve, reject) => {\n function updateAvatar (avatar) {\n that.$store.state.api.backendInteractor.updateAvatar({ avatar })\n .then((user) => {\n that.$store.commit('addNewUsers', [user])\n that.$store.commit('setCurrentUser', user)\n resolve()\n })\n .catch((err) => {\n reject(new Error(that.$t('upload.error.base') + ' ' + err.message))\n })\n }\n\n if (cropper) {\n cropper.getCroppedCanvas().toBlob(updateAvatar, file.type)\n } else {\n updateAvatar(file)\n }\n })\n },\n clearUploadError (slot) {\n this[slot + 'UploadError'] = null\n },\n submitBanner () {\n if (!this.bannerPreview) { return }\n\n this.bannerUploading = true\n this.$store.state.api.backendInteractor.updateBanner({ banner: this.banner })\n .then((user) => {\n this.$store.commit('addNewUsers', [user])\n this.$store.commit('setCurrentUser', user)\n this.bannerPreview = null\n })\n .catch((err) => {\n this.bannerUploadError = this.$t('upload.error.base') + ' ' + err.message\n })\n .then(() => { this.bannerUploading = false })\n },\n submitBg () {\n if (!this.backgroundPreview) { return }\n let background = this.background\n this.backgroundUploading = true\n this.$store.state.api.backendInteractor.updateBg({ background }).then((data) => {\n if (!data.error) {\n this.$store.commit('addNewUsers', [data])\n this.$store.commit('setCurrentUser', data)\n this.backgroundPreview = null\n } else {\n this.backgroundUploadError = this.$t('upload.error.base') + data.error\n }\n this.backgroundUploading = false\n })\n },\n importFollows (file) {\n return this.$store.state.api.backendInteractor.importFollows(file)\n .then((status) => {\n if (!status) {\n throw new Error('failed')\n }\n })\n },\n importBlocks (file) {\n return this.$store.state.api.backendInteractor.importBlocks(file)\n .then((status) => {\n if (!status) {\n throw new Error('failed')\n }\n })\n },\n generateExportableUsersContent (users) {\n // Get addresses\n return users.map((user) => {\n // check is it's a local user\n if (user && user.is_local) {\n // append the instance address\n // eslint-disable-next-line no-undef\n return user.screen_name + '@' + location.hostname\n }\n return user.screen_name\n }).join('\\n')\n },\n getFollowsContent () {\n return this.$store.state.api.backendInteractor.exportFriends({ id: this.$store.state.users.currentUser.id })\n .then(this.generateExportableUsersContent)\n },\n getBlocksContent () {\n return this.$store.state.api.backendInteractor.fetchBlocks()\n .then(this.generateExportableUsersContent)\n },\n confirmDelete () {\n this.deletingAccount = true\n },\n deleteAccount () {\n this.$store.state.api.backendInteractor.deleteAccount({ password: this.deleteAccountConfirmPasswordInput })\n .then((res) => {\n if (res.status === 'success') {\n this.$store.dispatch('logout')\n this.$router.push({ name: 'root' })\n } else {\n this.deleteAccountError = res.error\n }\n })\n },\n changePassword () {\n const params = {\n password: this.changePasswordInputs[0],\n newPassword: this.changePasswordInputs[1],\n newPasswordConfirmation: this.changePasswordInputs[2]\n }\n this.$store.state.api.backendInteractor.changePassword(params)\n .then((res) => {\n if (res.status === 'success') {\n this.changedPassword = true\n this.changePasswordError = false\n this.logout()\n } else {\n this.changedPassword = false\n this.changePasswordError = res.error\n }\n })\n },\n activateTab (tabName) {\n this.activeTab = tabName\n },\n logout () {\n this.$store.dispatch('logout')\n this.$router.replace('/')\n },\n revokeToken (id) {\n if (window.confirm(`${this.$i18n.t('settings.revoke_token')}?`)) {\n this.$store.dispatch('revokeToken', id)\n }\n },\n filterUnblockedUsers (userIds) {\n return reject(userIds, (userId) => {\n const user = this.$store.getters.findUser(userId)\n return !user || user.statusnet_blocking || user.id === this.$store.state.users.currentUser.id\n })\n },\n filterUnMutedUsers (userIds) {\n return reject(userIds, (userId) => {\n const user = this.$store.getters.findUser(userId)\n return !user || user.muted || user.id === this.$store.state.users.currentUser.id\n })\n },\n queryUserIds (query) {\n return this.$store.dispatch('searchUsers', query)\n .then((users) => map(users, 'id'))\n },\n blockUsers (ids) {\n return this.$store.dispatch('blockUsers', ids)\n },\n unblockUsers (ids) {\n return this.$store.dispatch('unblockUsers', ids)\n },\n muteUsers (ids) {\n return this.$store.dispatch('muteUsers', ids)\n },\n unmuteUsers (ids) {\n return this.$store.dispatch('unmuteUsers', ids)\n },\n identity (value) {\n return value\n }\n }\n}\n\nexport default UserSettings\n","import Cropper from 'cropperjs'\nimport 'cropperjs/dist/cropper.css'\n\nconst ImageCropper = {\n props: {\n trigger: {\n type: [String, window.Element],\n required: true\n },\n submitHandler: {\n type: Function,\n required: true\n },\n cropperOptions: {\n type: Object,\n default () {\n return {\n aspectRatio: 1,\n autoCropArea: 1,\n viewMode: 1,\n movable: false,\n zoomable: false,\n guides: false\n }\n }\n },\n mimes: {\n type: String,\n default: 'image/png, image/gif, image/jpeg, image/bmp, image/x-icon'\n },\n saveButtonLabel: {\n type: String\n },\n saveWithoutCroppingButtonlabel: {\n type: String\n },\n cancelButtonLabel: {\n type: String\n }\n },\n data () {\n return {\n cropper: undefined,\n dataUrl: undefined,\n filename: undefined,\n submitting: false,\n submitError: null\n }\n },\n computed: {\n saveText () {\n return this.saveButtonLabel || this.$t('image_cropper.save')\n },\n saveWithoutCroppingText () {\n return this.saveWithoutCroppingButtonlabel || this.$t('image_cropper.save_without_cropping')\n },\n cancelText () {\n return this.cancelButtonLabel || this.$t('image_cropper.cancel')\n },\n submitErrorMsg () {\n return this.submitError && this.submitError instanceof Error ? this.submitError.toString() : this.submitError\n }\n },\n methods: {\n destroy () {\n if (this.cropper) {\n this.cropper.destroy()\n }\n this.$refs.input.value = ''\n this.dataUrl = undefined\n this.$emit('close')\n },\n submit (cropping = true) {\n this.submitting = true\n this.avatarUploadError = null\n this.submitHandler(cropping && this.cropper, this.file)\n .then(() => this.destroy())\n .catch((err) => {\n this.submitError = err\n })\n .finally(() => {\n this.submitting = false\n })\n },\n pickImage () {\n this.$refs.input.click()\n },\n createCropper () {\n this.cropper = new Cropper(this.$refs.img, this.cropperOptions)\n },\n getTriggerDOM () {\n return typeof this.trigger === 'object' ? this.trigger : document.querySelector(this.trigger)\n },\n readFile () {\n const fileInput = this.$refs.input\n if (fileInput.files != null && fileInput.files[0] != null) {\n this.file = fileInput.files[0]\n let reader = new window.FileReader()\n reader.onload = (e) => {\n this.dataUrl = e.target.result\n this.$emit('open')\n }\n reader.readAsDataURL(this.file)\n this.$emit('changed', this.file, reader)\n }\n },\n clearError () {\n this.submitError = null\n }\n },\n mounted () {\n // listen for click event on trigger\n const trigger = this.getTriggerDOM()\n if (!trigger) {\n this.$emit('error', 'No image make trigger found.', 'user')\n } else {\n trigger.addEventListener('click', this.pickImage)\n }\n // listen for input file changes\n const fileInput = this.$refs.input\n fileInput.addEventListener('change', this.readFile)\n },\n beforeDestroy: function () {\n // remove the event listeners\n const trigger = this.getTriggerDOM()\n if (trigger) {\n trigger.removeEventListener('click', this.pickImage)\n }\n const fileInput = this.$refs.input\n fileInput.removeEventListener('change', this.readFile)\n }\n}\n\nexport default ImageCropper\n","import BasicUserCard from '../basic_user_card/basic_user_card.vue'\n\nconst BlockCard = {\n props: ['userId'],\n data () {\n return {\n progress: false\n }\n },\n computed: {\n user () {\n return this.$store.getters.findUser(this.userId)\n },\n blocked () {\n return this.user.statusnet_blocking\n }\n },\n components: {\n BasicUserCard\n },\n methods: {\n unblockUser () {\n this.progress = true\n this.$store.dispatch('unblockUser', this.user.id).then(() => {\n this.progress = false\n })\n },\n blockUser () {\n this.progress = true\n this.$store.dispatch('blockUser', this.user.id).then(() => {\n this.progress = false\n })\n }\n }\n}\n\nexport default BlockCard\n","import BasicUserCard from '../basic_user_card/basic_user_card.vue'\n\nconst MuteCard = {\n props: ['userId'],\n data () {\n return {\n progress: false\n }\n },\n computed: {\n user () {\n return this.$store.getters.findUser(this.userId)\n },\n muted () {\n return this.user.muted\n }\n },\n components: {\n BasicUserCard\n },\n methods: {\n unmuteUser () {\n this.progress = true\n this.$store.dispatch('unmuteUser', this.user.id).then(() => {\n this.progress = false\n })\n },\n muteUser () {\n this.progress = true\n this.$store.dispatch('muteUser', this.user.id).then(() => {\n this.progress = false\n })\n }\n }\n}\n\nexport default MuteCard\n","import List from '../list/list.vue'\nimport Checkbox from '../checkbox/checkbox.vue'\n\nconst SelectableList = {\n components: {\n List,\n Checkbox\n },\n props: {\n items: {\n type: Array,\n default: () => []\n },\n getKey: {\n type: Function,\n default: item => item.id\n }\n },\n data () {\n return {\n selected: []\n }\n },\n computed: {\n allKeys () {\n return this.items.map(this.getKey)\n },\n filteredSelected () {\n return this.allKeys.filter(key => this.selected.indexOf(key) !== -1)\n },\n allSelected () {\n return this.filteredSelected.length === this.items.length\n },\n noneSelected () {\n return this.filteredSelected.length === 0\n },\n someSelected () {\n return !this.allSelected && !this.noneSelected\n }\n },\n methods: {\n isSelected (item) {\n return this.filteredSelected.indexOf(this.getKey(item)) !== -1\n },\n toggle (checked, item) {\n const key = this.getKey(item)\n const oldChecked = this.isSelected(key)\n if (checked !== oldChecked) {\n if (checked) {\n this.selected.push(key)\n } else {\n this.selected.splice(this.selected.indexOf(key), 1)\n }\n }\n },\n toggleAll (value) {\n if (value) {\n this.selected = this.allKeys.slice(0)\n } else {\n this.selected = []\n }\n }\n }\n}\n\nexport default SelectableList\n","\n\n\n\n\n","const debounceMilliseconds = 500\n\nexport default {\n props: {\n query: { // function to query results and return a promise\n type: Function,\n required: true\n },\n filter: { // function to filter results in real time\n type: Function\n },\n placeholder: {\n type: String,\n default: 'Search...'\n }\n },\n data () {\n return {\n term: '',\n timeout: null,\n results: [],\n resultsVisible: false\n }\n },\n computed: {\n filtered () {\n return this.filter ? this.filter(this.results) : this.results\n }\n },\n watch: {\n term (val) {\n this.fetchResults(val)\n }\n },\n methods: {\n fetchResults (term) {\n clearTimeout(this.timeout)\n this.timeout = setTimeout(() => {\n this.results = []\n if (term) {\n this.query(term).then((results) => { this.results = results })\n }\n }, debounceMilliseconds)\n },\n onInputClick () {\n this.resultsVisible = true\n },\n onClickOutside () {\n this.resultsVisible = false\n }\n }\n}\n","const Importer = {\n props: {\n submitHandler: {\n type: Function,\n required: true\n },\n submitButtonLabel: {\n type: String,\n default () {\n return this.$t('importer.submit')\n }\n },\n successMessage: {\n type: String,\n default () {\n return this.$t('importer.success')\n }\n },\n errorMessage: {\n type: String,\n default () {\n return this.$t('importer.error')\n }\n }\n },\n data () {\n return {\n file: null,\n error: false,\n success: false,\n submitting: false\n }\n },\n methods: {\n change () {\n this.file = this.$refs.input.files[0]\n },\n submit () {\n this.dismiss()\n this.submitting = true\n this.submitHandler(this.file)\n .then(() => { this.success = true })\n .catch(() => { this.error = true })\n .finally(() => { this.submitting = false })\n },\n dismiss () {\n this.success = false\n this.error = false\n }\n }\n}\n\nexport default Importer\n","const Exporter = {\n props: {\n getContent: {\n type: Function,\n required: true\n },\n filename: {\n type: String,\n default: 'export.csv'\n },\n exportButtonLabel: {\n type: String,\n default () {\n return this.$t('exporter.export')\n }\n },\n processingMessage: {\n type: String,\n default () {\n return this.$t('exporter.processing')\n }\n }\n },\n data () {\n return {\n processing: false\n }\n },\n methods: {\n process () {\n this.processing = true\n this.getContent()\n .then((content) => {\n const fileToDownload = document.createElement('a')\n fileToDownload.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(content))\n fileToDownload.setAttribute('download', this.filename)\n fileToDownload.style.display = 'none'\n document.body.appendChild(fileToDownload)\n fileToDownload.click()\n document.body.removeChild(fileToDownload)\n // Add delay before hiding processing state since browser takes some time to handle file download\n setTimeout(() => { this.processing = false }, 2000)\n })\n }\n }\n}\n\nexport default Exporter\n","import RecoveryCodes from './mfa_backup_codes.vue'\nimport TOTP from './mfa_totp.vue'\nimport Confirm from './confirm.vue'\nimport VueQrcode from '@chenfengyuan/vue-qrcode'\nimport { mapState } from 'vuex'\n\nconst Mfa = {\n data: () => ({\n settings: { // current settings of MFA\n available: false,\n enabled: false,\n totp: false\n },\n setupState: { // setup mfa\n state: '', // state of setup. '' -> 'getBackupCodes' -> 'setupOTP' -> 'complete'\n setupOTPState: '' // state of setup otp. '' -> 'prepare' -> 'confirm' -> 'complete'\n },\n backupCodes: {\n getNewCodes: false,\n inProgress: false, // progress of fetch codes\n codes: []\n },\n otpSettings: { // pre-setup setting of OTP. secret key, qrcode url.\n provisioning_uri: '',\n key: ''\n },\n currentPassword: null,\n otpConfirmToken: null,\n error: null,\n readyInit: false\n }),\n components: {\n 'recovery-codes': RecoveryCodes,\n 'totp-item': TOTP,\n 'qrcode': VueQrcode,\n 'confirm': Confirm\n },\n computed: {\n canSetupOTP () {\n return (\n (this.setupInProgress && this.backupCodesPrepared) ||\n this.settings.enabled\n ) && !this.settings.totp && !this.setupOTPInProgress\n },\n setupInProgress () {\n return this.setupState.state !== '' && this.setupState.state !== 'complete'\n },\n setupOTPInProgress () {\n return this.setupState.state === 'setupOTP' && !this.completedOTP\n },\n prepareOTP () {\n return this.setupState.setupOTPState === 'prepare'\n },\n confirmOTP () {\n return this.setupState.setupOTPState === 'confirm'\n },\n completedOTP () {\n return this.setupState.setupOTPState === 'completed'\n },\n backupCodesPrepared () {\n return !this.backupCodes.inProgress && this.backupCodes.codes.length > 0\n },\n confirmNewBackupCodes () {\n return this.backupCodes.getNewCodes\n },\n ...mapState({\n backendInteractor: (state) => state.api.backendInteractor\n })\n },\n\n methods: {\n activateOTP () {\n if (!this.settings.enabled) {\n this.setupState.state = 'getBackupcodes'\n this.fetchBackupCodes()\n }\n },\n fetchBackupCodes () {\n this.backupCodes.inProgress = true\n this.backupCodes.codes = []\n\n return this.backendInteractor.generateMfaBackupCodes()\n .then((res) => {\n this.backupCodes.codes = res.codes\n this.backupCodes.inProgress = false\n })\n },\n getBackupCodes () { // get a new backup codes\n this.backupCodes.getNewCodes = true\n },\n confirmBackupCodes () { // confirm getting new backup codes\n this.fetchBackupCodes().then((res) => {\n this.backupCodes.getNewCodes = false\n })\n },\n cancelBackupCodes () { // cancel confirm form of new backup codes\n this.backupCodes.getNewCodes = false\n },\n\n // Setup OTP\n setupOTP () { // prepare setup OTP\n this.setupState.state = 'setupOTP'\n this.setupState.setupOTPState = 'prepare'\n this.backendInteractor.mfaSetupOTP()\n .then((res) => {\n this.otpSettings = res\n this.setupState.setupOTPState = 'confirm'\n })\n },\n doConfirmOTP () { // handler confirm enable OTP\n this.error = null\n this.backendInteractor.mfaConfirmOTP({\n token: this.otpConfirmToken,\n password: this.currentPassword\n })\n .then((res) => {\n if (res.error) {\n this.error = res.error\n return\n }\n this.completeSetup()\n })\n },\n\n completeSetup () {\n this.setupState.setupOTPState = 'complete'\n this.setupState.state = 'complete'\n this.currentPassword = null\n this.error = null\n this.fetchSettings()\n },\n cancelSetup () { // cancel setup\n this.setupState.setupOTPState = ''\n this.setupState.state = ''\n this.currentPassword = null\n this.error = null\n },\n // end Setup OTP\n\n // fetch settings from server\n async fetchSettings () {\n let result = await this.backendInteractor.fetchSettingsMFA()\n if (result.error) return\n this.settings = result.settings\n this.settings.available = true\n return result\n }\n },\n mounted () {\n this.fetchSettings().then(() => {\n this.readyInit = true\n })\n }\n}\nexport default Mfa\n","export default {\n props: {\n backupCodes: {\n type: Object,\n default: () => ({\n inProgress: false,\n codes: []\n })\n }\n },\n data: () => ({}),\n computed: {\n inProgress () { return this.backupCodes.inProgress },\n ready () { return this.backupCodes.codes.length > 0 },\n displayTitle () { return this.inProgress || this.ready }\n }\n}\n","import Confirm from './confirm.vue'\nimport { mapState } from 'vuex'\n\nexport default {\n props: ['settings'],\n data: () => ({\n error: false,\n currentPassword: '',\n deactivate: false,\n inProgress: false // progress peform request to disable otp method\n }),\n components: {\n 'confirm': Confirm\n },\n computed: {\n isActivated () {\n return this.settings.totp\n },\n ...mapState({\n backendInteractor: (state) => state.api.backendInteractor\n })\n },\n methods: {\n doActivate () {\n this.$emit('activate')\n },\n cancelDeactivate () { this.deactivate = false },\n doDeactivate () {\n this.error = null\n this.deactivate = true\n },\n confirmDeactivate () { // confirm deactivate TOTP method\n this.error = null\n this.inProgress = true\n this.backendInteractor.mfaDisableOTP({\n password: this.currentPassword\n })\n .then((res) => {\n this.inProgress = false\n if (res.error) {\n this.error = res.error\n return\n }\n this.deactivate = false\n this.$emit('deactivate')\n })\n }\n }\n}\n","const Confirm = {\n props: ['disabled'],\n data: () => ({}),\n methods: {\n confirm () { this.$emit('confirm') },\n cancel () { this.$emit('cancel') }\n }\n}\nexport default Confirm\n","import FollowRequestCard from '../follow_request_card/follow_request_card.vue'\n\nconst FollowRequests = {\n components: {\n FollowRequestCard\n },\n computed: {\n requests () {\n return this.$store.state.api.followRequests\n }\n }\n}\n\nexport default FollowRequests\n","import BasicUserCard from '../basic_user_card/basic_user_card.vue'\n\nconst FollowRequestCard = {\n props: ['user'],\n components: {\n BasicUserCard\n },\n methods: {\n approveUser () {\n this.$store.state.api.backendInteractor.approveUser(this.user.id)\n this.$store.dispatch('removeFollowRequest', this.user)\n },\n denyUser () {\n this.$store.state.api.backendInteractor.denyUser(this.user.id)\n this.$store.dispatch('removeFollowRequest', this.user)\n }\n }\n}\n\nexport default FollowRequestCard\n","import oauth from '../../services/new_api/oauth.js'\n\nconst oac = {\n props: ['code'],\n mounted () {\n if (this.code) {\n const { clientId, clientSecret } = this.$store.state.oauth\n\n oauth.getToken({\n clientId,\n clientSecret,\n instance: this.$store.state.instance.server,\n code: this.code\n }).then((result) => {\n this.$store.commit('setToken', result.access_token)\n this.$store.dispatch('loginUser', result.access_token)\n this.$router.push({ name: 'friends' })\n })\n }\n }\n}\n\nexport default oac\n","import { mapState, mapGetters, mapActions, mapMutations } from 'vuex'\nimport oauthApi from '../../services/new_api/oauth.js'\n\nconst LoginForm = {\n data: () => ({\n user: {},\n error: false\n }),\n computed: {\n isPasswordAuth () { return this.requiredPassword },\n isTokenAuth () { return this.requiredToken },\n ...mapState({\n registrationOpen: state => state.instance.registrationOpen,\n instance: state => state.instance,\n loggingIn: state => state.users.loggingIn,\n oauth: state => state.oauth\n }),\n ...mapGetters(\n 'authFlow', ['requiredPassword', 'requiredToken', 'requiredMFA']\n )\n },\n methods: {\n ...mapMutations('authFlow', ['requireMFA']),\n ...mapActions({ login: 'authFlow/login' }),\n submit () {\n this.isTokenAuth ? this.submitToken() : this.submitPassword()\n },\n submitToken () {\n const { clientId, clientSecret } = this.oauth\n const data = {\n clientId,\n clientSecret,\n instance: this.instance.server,\n commit: this.$store.commit\n }\n\n oauthApi.getOrCreateApp(data)\n .then((app) => { oauthApi.login({ ...app, ...data }) })\n },\n submitPassword () {\n const { clientId } = this.oauth\n const data = {\n clientId,\n oauth: this.oauth,\n instance: this.instance.server,\n commit: this.$store.commit\n }\n this.error = false\n\n oauthApi.getOrCreateApp(data).then((app) => {\n oauthApi.getTokenWithCredentials(\n {\n ...app,\n instance: data.instance,\n username: this.user.username,\n password: this.user.password\n }\n ).then((result) => {\n if (result.error) {\n if (result.error === 'mfa_required') {\n this.requireMFA({ app: app, settings: result })\n } else {\n this.error = result.error\n this.focusOnPasswordInput()\n }\n return\n }\n this.login(result).then(() => {\n this.$router.push({ name: 'friends' })\n })\n })\n })\n },\n clearError () { this.error = false },\n focusOnPasswordInput () {\n let passwordInput = this.$refs.passwordInput\n passwordInput.focus()\n passwordInput.setSelectionRange(0, passwordInput.value.length)\n }\n }\n}\n\nexport default LoginForm\n","import mfaApi from '../../services/new_api/mfa.js'\nimport { mapState, mapGetters, mapActions, mapMutations } from 'vuex'\n\nexport default {\n data: () => ({\n code: null,\n error: false\n }),\n computed: {\n ...mapGetters({\n authApp: 'authFlow/app',\n authSettings: 'authFlow/settings'\n }),\n ...mapState({ instance: 'instance' })\n },\n methods: {\n ...mapMutations('authFlow', ['requireTOTP', 'abortMFA']),\n ...mapActions({ login: 'authFlow/login' }),\n clearError () { this.error = false },\n submit () {\n const data = {\n app: this.authApp,\n instance: this.instance.server,\n mfaToken: this.authSettings.mfa_token,\n code: this.code\n }\n\n mfaApi.verifyRecoveryCode(data).then((result) => {\n if (result.error) {\n this.error = result.error\n this.code = null\n return\n }\n\n this.login(result).then(() => {\n this.$router.push({ name: 'friends' })\n })\n })\n }\n }\n}\n","import mfaApi from '../../services/new_api/mfa.js'\nimport { mapState, mapGetters, mapActions, mapMutations } from 'vuex'\nexport default {\n data: () => ({\n code: null,\n error: false\n }),\n computed: {\n ...mapGetters({\n authApp: 'authFlow/app',\n authSettings: 'authFlow/settings'\n }),\n ...mapState({ instance: 'instance' })\n },\n methods: {\n ...mapMutations('authFlow', ['requireRecovery', 'abortMFA']),\n ...mapActions({ login: 'authFlow/login' }),\n clearError () { this.error = false },\n submit () {\n const data = {\n app: this.authApp,\n instance: this.instance.server,\n mfaToken: this.authSettings.mfa_token,\n code: this.code\n }\n\n mfaApi.verifyOTPCode(data).then((result) => {\n if (result.error) {\n this.error = result.error\n this.code = null\n return\n }\n\n this.login(result).then(() => {\n this.$router.push({ name: 'friends' })\n })\n })\n }\n }\n}\n","import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'\n\nconst chatPanel = {\n props: [ 'floating' ],\n data () {\n return {\n currentMessage: '',\n channel: null,\n collapsed: true\n }\n },\n computed: {\n messages () {\n return this.$store.state.chat.messages\n }\n },\n methods: {\n submit (message) {\n this.$store.state.chat.channel.push('new_msg', { text: message }, 10000)\n this.currentMessage = ''\n },\n togglePanel () {\n this.collapsed = !this.collapsed\n },\n userProfileLink (user) {\n return generateProfileLink(user.id, user.username, this.$store.state.instance.restrictedNicknames)\n }\n }\n}\n\nexport default chatPanel\n","import apiService from '../../services/api/api.service.js'\nimport FollowCard from '../follow_card/follow_card.vue'\n\nconst WhoToFollow = {\n components: {\n FollowCard\n },\n data () {\n return {\n users: []\n }\n },\n mounted () {\n this.getWhoToFollow()\n },\n methods: {\n showWhoToFollow (reply) {\n reply.forEach((i, index) => {\n const user = {\n id: 0,\n name: i.display_name,\n screen_name: i.acct,\n profile_image_url: i.avatar || '/images/avi.png',\n profile_image_url_original: i.avatar || '/images/avi.png',\n statusnet_profile_url: i.url\n }\n this.users.push(user)\n\n this.$store.state.api.backendInteractor.fetchUser({ id: user.screen_name })\n .then((externalUser) => {\n if (!externalUser.error) {\n this.$store.commit('addNewUsers', [externalUser])\n user.id = externalUser.id\n }\n })\n })\n },\n getWhoToFollow () {\n const credentials = this.$store.state.users.currentUser.credentials\n if (credentials) {\n apiService.suggestions({ credentials: credentials })\n .then((reply) => {\n this.showWhoToFollow(reply)\n })\n }\n }\n }\n}\n\nexport default WhoToFollow\n","import InstanceSpecificPanel from '../instance_specific_panel/instance_specific_panel.vue'\nimport FeaturesPanel from '../features_panel/features_panel.vue'\nimport TermsOfServicePanel from '../terms_of_service_panel/terms_of_service_panel.vue'\n\nconst About = {\n components: {\n InstanceSpecificPanel,\n FeaturesPanel,\n TermsOfServicePanel\n },\n computed: {\n showFeaturesPanel () { return this.$store.state.instance.showFeaturesPanel }\n }\n}\n\nexport default About\n","const InstanceSpecificPanel = {\n computed: {\n instanceSpecificPanelContent () {\n return this.$store.state.instance.instanceSpecificPanelContent\n }\n }\n}\n\nexport default InstanceSpecificPanel\n","const FeaturesPanel = {\n computed: {\n chat: function () { return this.$store.state.instance.chatAvailable },\n gopher: function () { return this.$store.state.instance.gopherAvailable },\n whoToFollow: function () { return this.$store.state.instance.suggestionsEnabled },\n mediaProxy: function () { return this.$store.state.instance.mediaProxyAvailable },\n minimalScopesMode: function () { return this.$store.state.instance.minimalScopesMode },\n textlimit: function () { return this.$store.state.instance.textlimit }\n }\n}\n\nexport default FeaturesPanel\n","const TermsOfServicePanel = {\n computed: {\n content () {\n return this.$store.state.instance.tos\n }\n }\n}\n\nexport default TermsOfServicePanel\n","import UserPanel from './components/user_panel/user_panel.vue'\nimport NavPanel from './components/nav_panel/nav_panel.vue'\nimport Notifications from './components/notifications/notifications.vue'\nimport SearchBar from './components/search_bar/search_bar.vue'\nimport InstanceSpecificPanel from './components/instance_specific_panel/instance_specific_panel.vue'\nimport FeaturesPanel from './components/features_panel/features_panel.vue'\nimport WhoToFollowPanel from './components/who_to_follow_panel/who_to_follow_panel.vue'\nimport ChatPanel from './components/chat_panel/chat_panel.vue'\nimport MediaModal from './components/media_modal/media_modal.vue'\nimport SideDrawer from './components/side_drawer/side_drawer.vue'\nimport MobilePostStatusModal from './components/mobile_post_status_modal/mobile_post_status_modal.vue'\nimport MobileNav from './components/mobile_nav/mobile_nav.vue'\nimport UserReportingModal from './components/user_reporting_modal/user_reporting_modal.vue'\nimport { windowWidth } from './services/window_utils/window_utils'\n\nexport default {\n name: 'app',\n components: {\n UserPanel,\n NavPanel,\n Notifications,\n SearchBar,\n InstanceSpecificPanel,\n FeaturesPanel,\n WhoToFollowPanel,\n ChatPanel,\n MediaModal,\n SideDrawer,\n MobilePostStatusModal,\n MobileNav,\n UserReportingModal\n },\n data: () => ({\n mobileActivePanel: 'timeline',\n searchBarHidden: true,\n supportsMask: window.CSS && window.CSS.supports && (\n window.CSS.supports('mask-size', 'contain') ||\n window.CSS.supports('-webkit-mask-size', 'contain') ||\n window.CSS.supports('-moz-mask-size', 'contain') ||\n window.CSS.supports('-ms-mask-size', 'contain') ||\n window.CSS.supports('-o-mask-size', 'contain')\n )\n }),\n created () {\n // Load the locale from the storage\n this.$i18n.locale = this.$store.state.config.interfaceLanguage\n window.addEventListener('resize', this.updateMobileState)\n },\n destroyed () {\n window.removeEventListener('resize', this.updateMobileState)\n },\n computed: {\n currentUser () { return this.$store.state.users.currentUser },\n background () {\n return this.currentUser.background_image || this.$store.state.instance.background\n },\n enableMask () { return this.supportsMask && this.$store.state.instance.logoMask },\n logoStyle () {\n return {\n 'visibility': this.enableMask ? 'hidden' : 'visible'\n }\n },\n logoMaskStyle () {\n return this.enableMask ? {\n 'mask-image': `url(${this.$store.state.instance.logo})`\n } : {\n 'background-color': this.enableMask ? '' : 'transparent'\n }\n },\n logoBgStyle () {\n return Object.assign({\n 'margin': `${this.$store.state.instance.logoMargin} 0`,\n opacity: this.searchBarHidden ? 1 : 0\n }, this.enableMask ? {} : {\n 'background-color': this.enableMask ? '' : 'transparent'\n })\n },\n logo () { return this.$store.state.instance.logo },\n bgStyle () {\n return {\n 'background-image': `url(${this.background})`\n }\n },\n bgAppStyle () {\n return {\n '--body-background-image': `url(${this.background})`\n }\n },\n sitename () { return this.$store.state.instance.name },\n chat () { return this.$store.state.chat.channel.state === 'joined' },\n suggestionsEnabled () { return this.$store.state.instance.suggestionsEnabled },\n showInstanceSpecificPanel () {\n return this.$store.state.instance.showInstanceSpecificPanel &&\n !this.$store.state.config.hideISP &&\n this.$store.state.instance.instanceSpecificPanelContent\n },\n showFeaturesPanel () { return this.$store.state.instance.showFeaturesPanel },\n isMobileLayout () { return this.$store.state.interface.mobileLayout }\n },\n methods: {\n scrollToTop () {\n window.scrollTo(0, 0)\n },\n logout () {\n this.$router.replace('/main/public')\n this.$store.dispatch('logout')\n },\n onSearchBarToggled (hidden) {\n this.searchBarHidden = hidden\n },\n updateMobileState () {\n const mobileLayout = windowWidth() <= 800\n const changed = mobileLayout !== this.isMobileLayout\n if (changed) {\n this.$store.dispatch('setMobileLayout', mobileLayout)\n }\n }\n }\n}\n","import AuthForm from '../auth_form/auth_form.js'\nimport PostStatusForm from '../post_status_form/post_status_form.vue'\nimport UserCard from '../user_card/user_card.vue'\nimport { mapState } from 'vuex'\n\nconst UserPanel = {\n computed: {\n signedIn () { return this.user },\n ...mapState({ user: state => state.users.currentUser })\n },\n components: {\n AuthForm,\n PostStatusForm,\n UserCard\n }\n}\n\nexport default UserPanel\n","import followRequestFetcher from '../../services/follow_request_fetcher/follow_request_fetcher.service'\n\nconst NavPanel = {\n created () {\n if (this.currentUser && this.currentUser.locked) {\n const store = this.$store\n const credentials = store.state.users.currentUser.credentials\n\n followRequestFetcher.startFetching({ store, credentials })\n }\n },\n computed: {\n currentUser () {\n return this.$store.state.users.currentUser\n },\n chat () {\n return this.$store.state.chat.channel\n },\n followRequestCount () {\n return this.$store.state.api.followRequests.length\n }\n }\n}\n\nexport default NavPanel\n","const SearchBar = {\n data: () => ({\n searchTerm: undefined,\n hidden: true,\n error: false,\n loading: false\n }),\n watch: {\n '$route': function (route) {\n if (route.name === 'search') {\n this.searchTerm = route.query.query\n }\n }\n },\n methods: {\n find (searchTerm) {\n this.$router.push({ name: 'search', query: { query: searchTerm } })\n this.$refs.searchInput.focus()\n },\n toggleHidden () {\n this.hidden = !this.hidden\n this.$emit('toggled', this.hidden)\n this.$nextTick(() => {\n if (!this.hidden) {\n this.$refs.searchInput.focus()\n }\n })\n }\n }\n}\n\nexport default SearchBar\n","import apiService from '../../services/api/api.service.js'\nimport generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'\nimport { shuffle } from 'lodash'\n\nfunction showWhoToFollow (panel, reply) {\n const shuffled = shuffle(reply)\n\n panel.usersToFollow.forEach((toFollow, index) => {\n let user = shuffled[index]\n let img = user.avatar || '/images/avi.png'\n let name = user.acct\n\n toFollow.img = img\n toFollow.name = name\n\n panel.$store.state.api.backendInteractor.fetchUser({ id: name })\n .then((externalUser) => {\n if (!externalUser.error) {\n panel.$store.commit('addNewUsers', [externalUser])\n toFollow.id = externalUser.id\n }\n })\n })\n}\n\nfunction getWhoToFollow (panel) {\n var credentials = panel.$store.state.users.currentUser.credentials\n if (credentials) {\n panel.usersToFollow.forEach(toFollow => {\n toFollow.name = 'Loading...'\n })\n apiService.suggestions({ credentials: credentials })\n .then((reply) => {\n showWhoToFollow(panel, reply)\n })\n }\n}\n\nconst WhoToFollowPanel = {\n data: () => ({\n usersToFollow: new Array(3).fill().map(x => (\n {\n img: '/images/avi.png',\n name: '',\n id: 0\n }\n ))\n }),\n computed: {\n user: function () {\n return this.$store.state.users.currentUser.screen_name\n },\n suggestionsEnabled () {\n return this.$store.state.instance.suggestionsEnabled\n }\n },\n methods: {\n userProfileLink (id, name) {\n return generateProfileLink(id, name, this.$store.state.instance.restrictedNicknames)\n }\n },\n watch: {\n user: function (user, oldUser) {\n if (this.suggestionsEnabled) {\n getWhoToFollow(this)\n }\n }\n },\n mounted:\n function () {\n if (this.suggestionsEnabled) {\n getWhoToFollow(this)\n }\n }\n}\n\nexport default WhoToFollowPanel\n","import StillImage from '../still-image/still-image.vue'\nimport VideoAttachment from '../video_attachment/video_attachment.vue'\nimport fileTypeService from '../../services/file_type/file_type.service.js'\n\nconst MediaModal = {\n components: {\n StillImage,\n VideoAttachment\n },\n computed: {\n showing () {\n return this.$store.state.mediaViewer.activated\n },\n media () {\n return this.$store.state.mediaViewer.media\n },\n currentIndex () {\n return this.$store.state.mediaViewer.currentIndex\n },\n currentMedia () {\n return this.media[this.currentIndex]\n },\n canNavigate () {\n return this.media.length > 1\n },\n type () {\n return this.currentMedia ? fileTypeService.fileType(this.currentMedia.mimetype) : null\n }\n },\n methods: {\n hide () {\n this.$store.dispatch('closeMediaViewer')\n },\n goPrev () {\n if (this.canNavigate) {\n const prevIndex = this.currentIndex === 0 ? this.media.length - 1 : (this.currentIndex - 1)\n this.$store.dispatch('setCurrent', this.media[prevIndex])\n }\n },\n goNext () {\n if (this.canNavigate) {\n const nextIndex = this.currentIndex === this.media.length - 1 ? 0 : (this.currentIndex + 1)\n this.$store.dispatch('setCurrent', this.media[nextIndex])\n }\n },\n handleKeyupEvent (e) {\n if (this.showing && e.keyCode === 27) { // escape\n this.hide()\n }\n },\n handleKeydownEvent (e) {\n if (!this.showing) {\n return\n }\n\n if (e.keyCode === 39) { // arrow right\n this.goNext()\n } else if (e.keyCode === 37) { // arrow left\n this.goPrev()\n }\n }\n },\n mounted () {\n document.addEventListener('keyup', this.handleKeyupEvent)\n document.addEventListener('keydown', this.handleKeydownEvent)\n },\n destroyed () {\n document.removeEventListener('keyup', this.handleKeyupEvent)\n document.removeEventListener('keydown', this.handleKeydownEvent)\n }\n}\n\nexport default MediaModal\n","import UserCard from '../user_card/user_card.vue'\nimport { unseenNotificationsFromStore } from '../../services/notification_utils/notification_utils'\nimport GestureService from '../../services/gesture_service/gesture_service'\n\nconst SideDrawer = {\n props: [ 'logout' ],\n data: () => ({\n closed: true,\n closeGesture: undefined\n }),\n created () {\n this.closeGesture = GestureService.swipeGesture(GestureService.DIRECTION_LEFT, this.toggleDrawer)\n },\n components: { UserCard },\n computed: {\n currentUser () {\n return this.$store.state.users.currentUser\n },\n chat () { return this.$store.state.chat.channel.state === 'joined' },\n unseenNotifications () {\n return unseenNotificationsFromStore(this.$store)\n },\n unseenNotificationsCount () {\n return this.unseenNotifications.length\n },\n suggestionsEnabled () {\n return this.$store.state.instance.suggestionsEnabled\n },\n logo () {\n return this.$store.state.instance.logo\n },\n sitename () {\n return this.$store.state.instance.name\n },\n followRequestCount () {\n return this.$store.state.api.followRequests.length\n }\n },\n methods: {\n toggleDrawer () {\n this.closed = !this.closed\n },\n doLogout () {\n this.logout()\n this.toggleDrawer()\n },\n touchStart (e) {\n GestureService.beginSwipe(e, this.closeGesture)\n },\n touchMove (e) {\n GestureService.updateSwipe(e, this.closeGesture)\n }\n }\n}\n\nexport default SideDrawer\n","import PostStatusForm from '../post_status_form/post_status_form.vue'\nimport { debounce } from 'lodash'\n\nconst MobilePostStatusModal = {\n components: {\n PostStatusForm\n },\n data () {\n return {\n hidden: false,\n postFormOpen: false,\n scrollingDown: false,\n inputActive: false,\n oldScrollPos: 0,\n amountScrolled: 0\n }\n },\n created () {\n if (this.autohideFloatingPostButton) {\n this.activateFloatingPostButtonAutohide()\n }\n window.addEventListener('resize', this.handleOSK)\n },\n destroyed () {\n if (this.autohideFloatingPostButton) {\n this.deactivateFloatingPostButtonAutohide()\n }\n window.removeEventListener('resize', this.handleOSK)\n },\n computed: {\n currentUser () {\n return this.$store.state.users.currentUser\n },\n isHidden () {\n return this.autohideFloatingPostButton && (this.hidden || this.inputActive)\n },\n autohideFloatingPostButton () {\n return !!this.$store.state.config.autohideFloatingPostButton\n }\n },\n watch: {\n autohideFloatingPostButton: function (isEnabled) {\n if (isEnabled) {\n this.activateFloatingPostButtonAutohide()\n } else {\n this.deactivateFloatingPostButtonAutohide()\n }\n }\n },\n methods: {\n activateFloatingPostButtonAutohide () {\n window.addEventListener('scroll', this.handleScrollStart)\n window.addEventListener('scroll', this.handleScrollEnd)\n },\n deactivateFloatingPostButtonAutohide () {\n window.removeEventListener('scroll', this.handleScrollStart)\n window.removeEventListener('scroll', this.handleScrollEnd)\n },\n openPostForm () {\n this.postFormOpen = true\n this.hidden = true\n\n const el = this.$el.querySelector('textarea')\n this.$nextTick(function () {\n el.focus()\n })\n },\n closePostForm () {\n this.postFormOpen = false\n this.hidden = false\n },\n handleOSK () {\n // This is a big hack: we're guessing from changed window sizes if the\n // on-screen keyboard is active or not. This is only really important\n // for phones in portrait mode and it's more important to show the button\n // in normal scenarios on all phones, than it is to hide it when the\n // keyboard is active.\n // Guesswork based on https://www.mydevice.io/#compare-devices\n\n // for example, iphone 4 and android phones from the same time period\n const smallPhone = window.innerWidth < 350\n const smallPhoneKbOpen = smallPhone && window.innerHeight < 345\n\n const biggerPhone = !smallPhone && window.innerWidth < 450\n const biggerPhoneKbOpen = biggerPhone && window.innerHeight < 560\n if (smallPhoneKbOpen || biggerPhoneKbOpen) {\n this.inputActive = true\n } else {\n this.inputActive = false\n }\n },\n handleScrollStart: debounce(function () {\n if (window.scrollY > this.oldScrollPos) {\n this.hidden = true\n } else {\n this.hidden = false\n }\n this.oldScrollPos = window.scrollY\n }, 100, { leading: true, trailing: false }),\n\n handleScrollEnd: debounce(function () {\n this.hidden = false\n this.oldScrollPos = window.scrollY\n }, 100, { leading: false, trailing: true })\n }\n}\n\nexport default MobilePostStatusModal\n","import SideDrawer from '../side_drawer/side_drawer.vue'\nimport Notifications from '../notifications/notifications.vue'\nimport { unseenNotificationsFromStore } from '../../services/notification_utils/notification_utils'\nimport GestureService from '../../services/gesture_service/gesture_service'\n\nconst MobileNav = {\n components: {\n SideDrawer,\n Notifications\n },\n data: () => ({\n notificationsCloseGesture: undefined,\n notificationsOpen: false\n }),\n created () {\n this.notificationsCloseGesture = GestureService.swipeGesture(\n GestureService.DIRECTION_RIGHT,\n this.closeMobileNotifications,\n 50\n )\n },\n computed: {\n currentUser () {\n return this.$store.state.users.currentUser\n },\n unseenNotifications () {\n return unseenNotificationsFromStore(this.$store)\n },\n unseenNotificationsCount () {\n return this.unseenNotifications.length\n },\n sitename () { return this.$store.state.instance.name }\n },\n methods: {\n toggleMobileSidebar () {\n this.$refs.sideDrawer.toggleDrawer()\n },\n openMobileNotifications () {\n this.notificationsOpen = true\n },\n closeMobileNotifications () {\n if (this.notificationsOpen) {\n // make sure to mark notifs seen only when the notifs were open and not\n // from close-calls.\n this.notificationsOpen = false\n this.markNotificationsAsSeen()\n }\n },\n notificationsTouchStart (e) {\n GestureService.beginSwipe(e, this.notificationsCloseGesture)\n },\n notificationsTouchMove (e) {\n GestureService.updateSwipe(e, this.notificationsCloseGesture)\n },\n scrollToTop () {\n window.scrollTo(0, 0)\n },\n logout () {\n this.$router.replace('/main/public')\n this.$store.dispatch('logout')\n },\n markNotificationsAsSeen () {\n this.$refs.notifications.markAsSeen()\n },\n onScroll ({ target: { scrollTop, clientHeight, scrollHeight } }) {\n if (this.$store.state.config.autoLoad && scrollTop + clientHeight >= scrollHeight) {\n this.$refs.notifications.fetchOlderNotifications()\n }\n }\n },\n watch: {\n $route () {\n // handles closing notificaitons when you press any router-link on the\n // notifications.\n this.closeMobileNotifications()\n }\n }\n}\n\nexport default MobileNav\n","\nimport Status from '../status/status.vue'\nimport List from '../list/list.vue'\nimport Checkbox from '../checkbox/checkbox.vue'\n\nconst UserReportingModal = {\n components: {\n Status,\n List,\n Checkbox\n },\n data () {\n return {\n comment: '',\n forward: false,\n statusIdsToReport: [],\n processing: false,\n error: false\n }\n },\n computed: {\n isLoggedIn () {\n return !!this.$store.state.users.currentUser\n },\n isOpen () {\n return this.isLoggedIn && this.$store.state.reports.modalActivated\n },\n userId () {\n return this.$store.state.reports.userId\n },\n user () {\n return this.$store.getters.findUser(this.userId)\n },\n remoteInstance () {\n return !this.user.is_local && this.user.screen_name.substr(this.user.screen_name.indexOf('@') + 1)\n },\n statuses () {\n return this.$store.state.reports.statuses\n }\n },\n watch: {\n userId: 'resetState'\n },\n methods: {\n resetState () {\n // Reset state\n this.comment = ''\n this.forward = false\n this.statusIdsToReport = []\n this.processing = false\n this.error = false\n },\n closeModal () {\n this.$store.dispatch('closeUserReportingModal')\n },\n reportUser () {\n this.processing = true\n this.error = false\n const params = {\n userId: this.userId,\n comment: this.comment,\n forward: this.forward,\n statusIds: this.statusIdsToReport\n }\n this.$store.state.api.backendInteractor.reportUser(params)\n .then(() => {\n this.processing = false\n this.resetState()\n this.closeModal()\n })\n .catch(() => {\n this.processing = false\n this.error = true\n })\n },\n clearError () {\n this.error = false\n },\n isChecked (statusId) {\n return this.statusIdsToReport.indexOf(statusId) !== -1\n },\n toggleStatus (checked, statusId) {\n if (checked === this.isChecked(statusId)) {\n return\n }\n\n if (checked) {\n this.statusIdsToReport.push(statusId)\n } else {\n this.statusIdsToReport.splice(this.statusIdsToReport.indexOf(statusId), 1)\n }\n },\n resize (e) {\n const target = e.target || e\n if (!(target instanceof window.Element)) { return }\n // Auto is needed to make textbox shrink when removing lines\n target.style.height = 'auto'\n target.style.height = `${target.scrollHeight}px`\n if (target.value === '') {\n target.style.height = null\n }\n }\n }\n}\n\nexport default UserReportingModal\n","import apiService from '../api/api.service.js'\nimport timelineFetcherService from '../timeline_fetcher/timeline_fetcher.service.js'\nimport notificationsFetcher from '../notifications_fetcher/notifications_fetcher.service.js'\n\nconst backendInteractorService = credentials => {\n const fetchStatus = ({ id }) => {\n return apiService.fetchStatus({ id, credentials })\n }\n\n const fetchConversation = ({ id }) => {\n return apiService.fetchConversation({ id, credentials })\n }\n\n const fetchFriends = ({ id, maxId, sinceId, limit }) => {\n return apiService.fetchFriends({ id, maxId, sinceId, limit, credentials })\n }\n\n const exportFriends = ({ id }) => {\n return apiService.exportFriends({ id, credentials })\n }\n\n const fetchFollowers = ({ id, maxId, sinceId, limit }) => {\n return apiService.fetchFollowers({ id, maxId, sinceId, limit, credentials })\n }\n\n const fetchUser = ({ id }) => {\n return apiService.fetchUser({ id, credentials })\n }\n\n const fetchUserRelationship = ({ id }) => {\n return apiService.fetchUserRelationship({ id, credentials })\n }\n\n const followUser = (id) => {\n return apiService.followUser({ credentials, id })\n }\n\n const unfollowUser = (id) => {\n return apiService.unfollowUser({ credentials, id })\n }\n\n const blockUser = (id) => {\n return apiService.blockUser({ credentials, id })\n }\n\n const unblockUser = (id) => {\n return apiService.unblockUser({ credentials, id })\n }\n\n const approveUser = (id) => {\n return apiService.approveUser({ credentials, id })\n }\n\n const denyUser = (id) => {\n return apiService.denyUser({ credentials, id })\n }\n\n const startFetchingTimeline = ({ timeline, store, userId = false, tag }) => {\n return timelineFetcherService.startFetching({ timeline, store, credentials, userId, tag })\n }\n\n const startFetchingNotifications = ({ store }) => {\n return notificationsFetcher.startFetching({ store, credentials })\n }\n\n // eslint-disable-next-line camelcase\n const tagUser = ({ screen_name }, tag) => {\n return apiService.tagUser({ screen_name, tag, credentials })\n }\n\n // eslint-disable-next-line camelcase\n const untagUser = ({ screen_name }, tag) => {\n return apiService.untagUser({ screen_name, tag, credentials })\n }\n\n // eslint-disable-next-line camelcase\n const addRight = ({ screen_name }, right) => {\n return apiService.addRight({ screen_name, right, credentials })\n }\n\n // eslint-disable-next-line camelcase\n const deleteRight = ({ screen_name }, right) => {\n return apiService.deleteRight({ screen_name, right, credentials })\n }\n\n // eslint-disable-next-line camelcase\n const setActivationStatus = ({ screen_name }, status) => {\n return apiService.setActivationStatus({ screen_name, status, credentials })\n }\n\n // eslint-disable-next-line camelcase\n const deleteUser = ({ screen_name }) => {\n return apiService.deleteUser({ screen_name, credentials })\n }\n\n const vote = (pollId, choices) => {\n return apiService.vote({ credentials, pollId, choices })\n }\n\n const fetchPoll = (pollId) => {\n return apiService.fetchPoll({ credentials, pollId })\n }\n\n const updateNotificationSettings = ({ settings }) => {\n return apiService.updateNotificationSettings({ credentials, settings })\n }\n\n const fetchMutes = () => apiService.fetchMutes({ credentials })\n const muteUser = (id) => apiService.muteUser({ credentials, id })\n const unmuteUser = (id) => apiService.unmuteUser({ credentials, id })\n const subscribeUser = (id) => apiService.subscribeUser({ credentials, id })\n const unsubscribeUser = (id) => apiService.unsubscribeUser({ credentials, id })\n const fetchBlocks = () => apiService.fetchBlocks({ credentials })\n const fetchFollowRequests = () => apiService.fetchFollowRequests({ credentials })\n const fetchOAuthTokens = () => apiService.fetchOAuthTokens({ credentials })\n const revokeOAuthToken = (id) => apiService.revokeOAuthToken({ id, credentials })\n const fetchPinnedStatuses = (id) => apiService.fetchPinnedStatuses({ credentials, id })\n const pinOwnStatus = (id) => apiService.pinOwnStatus({ credentials, id })\n const unpinOwnStatus = (id) => apiService.unpinOwnStatus({ credentials, id })\n const muteConversation = (id) => apiService.muteConversation({ credentials, id })\n const unmuteConversation = (id) => apiService.unmuteConversation({ credentials, id })\n\n const getCaptcha = () => apiService.getCaptcha()\n const register = (params) => apiService.register({ credentials, params })\n const updateAvatar = ({ avatar }) => apiService.updateAvatar({ credentials, avatar })\n const updateBg = ({ background }) => apiService.updateBg({ credentials, background })\n const updateBanner = ({ banner }) => apiService.updateBanner({ credentials, banner })\n const updateProfile = ({ params }) => apiService.updateProfile({ credentials, params })\n\n const importBlocks = (file) => apiService.importBlocks({ file, credentials })\n const importFollows = (file) => apiService.importFollows({ file, credentials })\n\n const deleteAccount = ({ password }) => apiService.deleteAccount({ credentials, password })\n const changePassword = ({ password, newPassword, newPasswordConfirmation }) =>\n apiService.changePassword({ credentials, password, newPassword, newPasswordConfirmation })\n\n const fetchSettingsMFA = () => apiService.settingsMFA({ credentials })\n const generateMfaBackupCodes = () => apiService.generateMfaBackupCodes({ credentials })\n const mfaSetupOTP = () => apiService.mfaSetupOTP({ credentials })\n const mfaConfirmOTP = ({ password, token }) => apiService.mfaConfirmOTP({ credentials, password, token })\n const mfaDisableOTP = ({ password }) => apiService.mfaDisableOTP({ credentials, password })\n\n const fetchFavoritedByUsers = (id) => apiService.fetchFavoritedByUsers({ id })\n const fetchRebloggedByUsers = (id) => apiService.fetchRebloggedByUsers({ id })\n const reportUser = (params) => apiService.reportUser({ credentials, ...params })\n\n const favorite = (id) => apiService.favorite({ id, credentials })\n const unfavorite = (id) => apiService.unfavorite({ id, credentials })\n const retweet = (id) => apiService.retweet({ id, credentials })\n const unretweet = (id) => apiService.unretweet({ id, credentials })\n const search2 = ({ q, resolve, limit, offset, following }) =>\n apiService.search2({ credentials, q, resolve, limit, offset, following })\n const searchUsers = (query) => apiService.searchUsers({ query, credentials })\n\n const backendInteractorServiceInstance = {\n fetchStatus,\n fetchConversation,\n fetchFriends,\n exportFriends,\n fetchFollowers,\n followUser,\n unfollowUser,\n blockUser,\n unblockUser,\n fetchUser,\n fetchUserRelationship,\n verifyCredentials: apiService.verifyCredentials,\n startFetchingTimeline,\n startFetchingNotifications,\n fetchMutes,\n muteUser,\n unmuteUser,\n subscribeUser,\n unsubscribeUser,\n fetchBlocks,\n fetchOAuthTokens,\n revokeOAuthToken,\n fetchPinnedStatuses,\n pinOwnStatus,\n unpinOwnStatus,\n muteConversation,\n unmuteConversation,\n tagUser,\n untagUser,\n addRight,\n deleteRight,\n deleteUser,\n setActivationStatus,\n register,\n getCaptcha,\n updateAvatar,\n updateBg,\n updateBanner,\n updateProfile,\n importBlocks,\n importFollows,\n deleteAccount,\n changePassword,\n fetchSettingsMFA,\n generateMfaBackupCodes,\n mfaSetupOTP,\n mfaConfirmOTP,\n mfaDisableOTP,\n fetchFollowRequests,\n approveUser,\n denyUser,\n vote,\n fetchPoll,\n fetchFavoritedByUsers,\n fetchRebloggedByUsers,\n reportUser,\n favorite,\n unfavorite,\n retweet,\n unretweet,\n updateNotificationSettings,\n search2,\n searchUsers\n }\n\n return backendInteractorServiceInstance\n}\n\nexport default backendInteractorService\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","/* 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","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-09f1050a\\\",\\\"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","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","import { filter, sortBy } 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].filter(_ => _))\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 visibleNotificationsFromStore = (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(visibleNotificationsFromStore(store), ({ seen }) => !seen)\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!./follow_card.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./follow_card.js\"\nimport __vue_script__ from \"!!babel-loader!./follow_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-5637bff8\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./follow_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","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!./list.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!../../../node_modules/vue-loader/lib/selector?type=script&index=0!./list.vue\"\nimport __vue_script__ from \"!!babel-loader!../../../node_modules/vue-loader/lib/selector?type=script&index=0!./list.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-c1790f52\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./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:\"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 }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('video',{staticClass:\"video\",attrs:{\"src\":_vm.attachment.url,\"loop\":_vm.loopVideo,\"controls\":_vm.controls,\"playsinline\":\"\"},on:{\"loadeddata\":_vm.onVideoDataLoad}})}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","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}})],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 }","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.hidePostStatsLocal && _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.hidePostStatsLocal && _vm.status.fave_num > 0)?_c('span',[_vm._v(_vm._s(_vm.status.fave_num))]):_vm._e()])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","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.hidePostStatsLocal && _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.hidePostStatsLocal && _vm.status.repeat_num > 0)?_c('span',[_vm._v(_vm._s(_vm.status.repeat_num))]):_vm._e()]):_vm._e()}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","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 }","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 }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return (_vm.canDelete || _vm.canMute || _vm.canPin)?_c('v-popover',{staticClass:\"extra-button-popover\",attrs:{\"trigger\":\"click\",\"placement\":\"top\",\"offset\":5,\"container\":false}},[_c('div',{attrs:{\"slot\":\"popover\"},slot:\"popover\"},[_c('div',{staticClass:\"dropdown-menu\"},[(_vm.canMute && !_vm.status.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.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('div',{staticClass:\"button-icon\"},[_c('i',{staticClass:\"icon-ellipsis\"})])]):_vm._e()}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","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:\"btn btn-default\",attrs:{\"title\":_vm.$t('tool_tip.media_upload')}},[(_vm.uploading)?_c('i',{staticClass:\"icon-spin4 animate-spin\"}):_vm._e(),_vm._v(\" \"),(!_vm.uploading)?_c('i',{staticClass:\"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 }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return (!_vm.showNothing)?_c('div',{staticClass:\"scope-selector\"},[(_vm.showDirect)?_c('i',{staticClass:\"icon-mail-alt\",class:_vm.css.direct,attrs:{\"title\":_vm.$t('post_status.scope.direct')},on:{\"click\":function($event){_vm.changeVis('direct')}}}):_vm._e(),_vm._v(\" \"),(_vm.showPrivate)?_c('i',{staticClass:\"icon-lock\",class:_vm.css.private,attrs:{\"title\":_vm.$t('post_status.scope.private')},on:{\"click\":function($event){_vm.changeVis('private')}}}):_vm._e(),_vm._v(\" \"),(_vm.showUnlisted)?_c('i',{staticClass:\"icon-lock-open-alt\",class:_vm.css.unlisted,attrs:{\"title\":_vm.$t('post_status.scope.unlisted')},on:{\"click\":function($event){_vm.changeVis('unlisted')}}}):_vm._e(),_vm._v(\" \"),(_vm.showPublic)?_c('i',{staticClass:\"icon-globe\",class:_vm.css.public,attrs:{\"title\":_vm.$t('post_status.scope.public')},on:{\"click\":function($event){_vm.changeVis('public')}}}):_vm._e()]):_vm._e()}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"emoji-input\"},[_vm._t(\"default\"),_vm._v(\" \"),_c('div',{ref:\"panel\",staticClass:\"autocomplete-panel\",class:{ hide: !_vm.showPopup }},[_c('div',{staticClass:\"autocomplete-panel-body\"},_vm._l((_vm.suggestions),function(suggestion,index){return _c('div',{key:index,staticClass:\"autocomplete-item\",class:{ highlighted: suggestion.highlighted },on:{\"click\":function($event){$event.stopPropagation();$event.preventDefault();_vm.onClick($event, suggestion)}}},[_c('span',{staticClass:\"image\"},[(suggestion.img)?_c('img',{attrs:{\"src\":suggestion.img}}):_c('span',[_vm._v(_vm._s(suggestion.replacement))])]),_vm._v(\" \"),_c('div',{staticClass:\"label\"},[_c('span',{staticClass:\"displayText\"},[_vm._v(_vm._s(suggestion.displayText))]),_vm._v(\" \"),_c('span',{staticClass:\"detailText\"},[_vm._v(_vm._s(suggestion.detailText))])])])}),0)])],2)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","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 }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"sticker-picker\"},[_c('div',{staticClass:\"sticker-picker-panel\"},[_c('tab-switcher',{attrs:{\"render-only-focused\":true}},_vm._l((_vm.pack),function(stickerpack){return _c('div',{key:stickerpack.path,staticClass:\"sticker-picker-content\",attrs:{\"image-tooltip\":stickerpack.meta.title,\"image\":stickerpack.path + stickerpack.meta.tabIcon}},_vm._l((stickerpack.meta.stickers),function(sticker){return _c('div',{key:sticker,staticClass:\"sticker\",on:{\"click\":function($event){_vm.pick(stickerpack.path + sticker, stickerpack.meta.title)}}},[_c('img',{attrs:{\"src\":stickerpack.path + sticker}})])}),0)}),0)],1)])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{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:{\"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',{staticClass:\"form-control main-input\",attrs:{\"suggest\":_vm.emojiUserSuggestor},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],\"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',{staticClass:\"form-bottom\"},[_c('div',{staticClass:\"form-bottom-left\"},[_c('media-upload',{ref:\"mediaUpload\",attrs:{\"drop-files\":_vm.dropFiles},on:{\"uploading\":_vm.disableSubmit,\"uploaded\":_vm.addMediaFile,\"upload-failed\":_vm.uploadFailed}}),_vm._v(\" \"),(_vm.stickersAvailable)?_c('div',{staticClass:\"sticker-icon\"},[_c('i',{staticClass:\"icon-picture btn btn-default\",class:{ selected: _vm.stickerPickerVisible },attrs:{\"title\":_vm.$t('stickers.add_sticker')},on:{\"click\":_vm.toggleStickerPicker}})]):_vm._e(),_vm._v(\" \"),(_vm.pollsAvailable)?_c('div',{staticClass:\"poll-icon\"},[_c('i',{staticClass:\"icon-chart-bar btn btn-default\",class:_vm.pollFormVisible && 'selected',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('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.newStatus.nsfw),expression:\"newStatus.nsfw\"}],attrs:{\"id\":\"filesSensitive\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.newStatus.nsfw)?_vm._i(_vm.newStatus.nsfw,null)>-1:(_vm.newStatus.nsfw)},on:{\"change\":function($event){var $$a=_vm.newStatus.nsfw,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.$set(_vm.newStatus, \"nsfw\", $$a.concat([$$v])))}else{$$i>-1&&(_vm.$set(_vm.newStatus, \"nsfw\", $$a.slice(0,$$i).concat($$a.slice($$i+1))))}}else{_vm.$set(_vm.newStatus, \"nsfw\", $$c)}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"filesSensitive\"}},[_vm._v(_vm._s(_vm.$t('post_status.attachments_sensitive')))])]):_vm._e()],1),_vm._v(\" \"),(_vm.stickerPickerVisible)?_c('sticker-picker',{ref:\"stickerPicker\",on:{\"uploaded\":_vm.addMediaFile}}):_vm._e()],1)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","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 }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"remote-follow\"},[_c('form',{attrs:{\"method\":\"POST\",\"action\":_vm.subscribeUrl}},[_c('input',{attrs:{\"type\":\"hidden\",\"name\":\"nickname\"},domProps:{\"value\":_vm.user.screen_name}}),_vm._v(\" \"),_c('input',{attrs:{\"type\":\"hidden\",\"name\":\"profile\",\"value\":\"\"}}),_vm._v(\" \"),_c('button',{staticClass:\"remote-button\",attrs:{\"click\":\"submit\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.remote_follow'))+\"\\n \")])])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","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 }","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 }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('v-popover',{staticClass:\"moderation-tools-popover\",attrs:{\"trigger\":\"click\",\"container\":false,\"placement\":\"bottom-end\",\"offset\":5},on:{\"show\":function($event){_vm.showDropDown = true},\"hide\":function($event){_vm.showDropDown = false}}},[_c('div',{attrs:{\"slot\":\"popover\"},slot:\"popover\"},[_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:{ pressed: _vm.showDropDown }},[_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 }","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,style:(_vm.style)},[_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()],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.hideUserStatsLocal && !_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\"},[(!_vm.user.following)?_c('div',[_c('button',{staticClass:\"btn btn-default btn-block\",attrs:{\"disabled\":_vm.followRequestInProgress,\"title\":_vm.followRequestSent ? _vm.$t('user_card.follow_again') : ''},on:{\"click\":_vm.followUser}},[(_vm.followRequestInProgress)?[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.follow_progress'))+\"\\n \")]:(_vm.followRequestSent)?[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.follow_sent'))+\"\\n \")]:[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.follow'))+\"\\n \")]],2)]):(_vm.followRequestInProgress)?_c('div',[_c('button',{staticClass:\"btn btn-default btn-block pressed\",attrs:{\"disabled\":\"\",\"title\":_vm.$t('user_card.follow_unfollow')},on:{\"click\":_vm.unfollowUser}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.follow_progress'))+\"\\n \")])]):_c('div',{staticClass:\"btn-group\"},[_c('button',{staticClass:\"btn btn-default pressed\",attrs:{\"title\":_vm.$t('user_card.follow_unfollow')},on:{\"click\":_vm.unfollowUser}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.following'))+\"\\n \")]),_vm._v(\" \"),(!_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 pressed\",attrs:{\"click\":_vm.unsubscribeUser,\"title\":_vm.$t('user_card.unsubscribe')}},[_c('i',{staticClass:\"icon-bell-ringing-o\"})])],1),_vm._v(\" \"),_c('div',[(_vm.user.muted)?_c('button',{staticClass:\"btn btn-default btn-block pressed\",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',[(_vm.user.statusnet_blocking)?_c('button',{staticClass:\"btn btn-default btn-block pressed\",on:{\"click\":_vm.unblockUser}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.blocked'))+\"\\n \")]):_c('button',{staticClass:\"btn btn-default btn-block\",on:{\"click\":_vm.blockUser}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.block'))+\"\\n \")])]),_vm._v(\" \"),_c('div',[_c('button',{staticClass:\"btn btn-default btn-block\",on:{\"click\":_vm.reportUser}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.report'))+\"\\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.hideUserStatsLocal && _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.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.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 }","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.rowHeight(row.length))},_vm._l((row),function(attachment){return _c('attachment',{key:attachment.id,attrs:{\"set-media\":_vm.setMedia,\"nsfw\":_vm.nsfw,\"attachment\":attachment,\"allow-play\":false}})}),1)}),0)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","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 }","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 }","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\"},[_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)},\"mouseenter\":function($event){$event.preventDefault();$event.stopPropagation();_vm.replyEnter(_vm.status.in_reply_to_status_id, $event)},\"mouseleave\":function($event){$event.preventDefault();$event.stopPropagation();_vm.replyLeave()}}},[(!_vm.isPreview)?_c('i',{staticClass:\"button-icon icon-reply\"}):_vm._e(),_vm._v(\" \"),_c('span',{staticClass:\"faint-link 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)?_c('div',{staticClass:\"replies\"},[(_vm.replies && _vm.replies.length)?_c('span',{staticClass:\"faint\"},[_vm._v(_vm._s(_vm.$t('status.replies_list')))]):_vm._e(),_vm._v(\" \"),(_vm.replies)?_vm._l((_vm.replies),function(reply){return _c('span',{key:reply.id,staticClass:\"reply-link faint\"},[_c('a',{attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();_vm.gotoOriginal(reply.id)},\"mouseenter\":function($event){_vm.replyEnter(reply.id, $event)},\"mouseout\":function($event){_vm.replyLeave()}}},[_vm._v(_vm._s(reply.name))])])}):_vm._e()],2):_vm._e()])]):_vm._e(),_vm._v(\" \"),(_vm.showPreview)?_c('div',{staticClass:\"status-preview-container\"},[(_vm.preview)?_c('status',{staticClass:\"status-preview\",attrs:{\"is-preview\":true,\"statusoid\":_vm.preview,\"compact\":true}}):_c('div',{staticClass:\"status-preview status-preview-loading\"},[_c('i',{staticClass:\"icon-spin4 animate-spin\"})])],1):_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(_vm._s(_vm.$t(\"general.show_more\")))]):_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.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('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('post-status-form',{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 }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"timeline panel-default\",class:[_vm.isExpanded ? 'panel' : 'panel-disabled']},[(_vm.isExpanded)?_c('div',{staticClass:\"panel-heading conversation-heading\"},[_c('span',{staticClass:\"title\"},[_vm._v(\" \"+_vm._s(_vm.$t('timeline.conversation'))+\" \")]),_vm._v(\" \"),(_vm.collapsable)?_c('span',[_c('a',{attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();return _vm.toggleExpanded($event)}}},[_vm._v(_vm._s(_vm.$t('timeline.collapse')))])]):_vm._e()]):_vm._e(),_vm._v(\" \"),_vm._l((_vm.conversation),function(status){return _c('status',{key:status.id,staticClass:\"status-fadein panel-body\",attrs:{\"inline-expanded\":_vm.collapsable && _vm.isExpanded,\"statusoid\":status,\"expandable\":!_vm.isExpanded,\"show-pinned\":_vm.pinnedStatusIdsObject && _vm.pinnedStatusIdsObject[status.id],\"focused\":_vm.focused(status.id),\"in-conversation\":_vm.isExpanded,\"highlight\":_vm.getHighlight(),\"replies\":_vm.getReplies(status.id)},on:{\"goto\":_vm.setHighlight,\"toggleExpanded\":_vm.toggleExpanded}})})],2)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{class:_vm.classes.root},[_c('div',{class:_vm.classes.header},[_c('div',{staticClass:\"title\"},[_vm._v(\"\\n \"+_vm._s(_vm.title)+\"\\n \")]),_vm._v(\" \"),(_vm.timelineError)?_c('div',{staticClass:\"loadmore-error alert error\",on:{\"click\":function($event){$event.preventDefault();}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('timeline.error_fetching'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(_vm.timeline.newStatusCount > 0 && !_vm.timelineError)?_c('button',{staticClass:\"loadmore-button\",on:{\"click\":function($event){$event.preventDefault();return _vm.showNewStatuses($event)}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('timeline.show_new'))+_vm._s(_vm.newStatusCountStr)+\"\\n \")]):_vm._e(),_vm._v(\" \"),(!_vm.timeline.newStatusCount > 0 && !_vm.timelineError)?_c('div',{staticClass:\"loadmore-text faint\",on:{\"click\":function($event){$event.preventDefault();}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('timeline.up_to_date'))+\"\\n \")]):_vm._e()]),_vm._v(\" \"),_c('div',{class:_vm.classes.body},[_c('div',{staticClass:\"timeline\"},[_vm._l((_vm.pinnedStatusIds),function(statusId){return [(_vm.timeline.statusesObject[statusId])?_c('conversation',{key:statusId + '-pinned',staticClass:\"status-fadein\",attrs:{\"statusoid\":_vm.timeline.statusesObject[statusId],\"collapsable\":true,\"pinned-status-ids-object\":_vm.pinnedStatusIdsObject}}):_vm._e()]}),_vm._v(\" \"),_vm._l((_vm.timeline.visibleStatuses),function(status){return [(!_vm.excludedStatusIdsObject[status.id])?_c('conversation',{key:status.id,staticClass:\"status-fadein\",attrs:{\"statusoid\":status,\"collapsable\":true}}):_vm._e()]})],2)]),_vm._v(\" \"),_c('div',{class:_vm.classes.footer},[(_vm.count===0)?_c('div',{staticClass:\"new-status-notification text-center panel-footer faint\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('timeline.no_statuses'))+\"\\n \")]):(_vm.bottomedOut)?_c('div',{staticClass:\"new-status-notification text-center panel-footer faint\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('timeline.no_more_statuses'))+\"\\n \")]):(!_vm.timeline.loading)?_c('a',{attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();_vm.fetchOlderStatuses()}}},[_c('div',{staticClass:\"new-status-notification text-center panel-footer\"},[_vm._v(_vm._s(_vm.$t('timeline.load_older')))])]):_c('div',{staticClass:\"new-status-notification text-center panel-footer\"},[_c('i',{staticClass:\"icon-spin3 animate-spin\"})])])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('Timeline',{attrs:{\"title\":_vm.$t('nav.public_tl'),\"timeline\":_vm.timeline,\"timeline-name\":'public'}})}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('Timeline',{attrs:{\"title\":_vm.$t('nav.twkn'),\"timeline\":_vm.timeline,\"timeline-name\":'publicAndExternal'}})}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('Timeline',{attrs:{\"title\":_vm.$t('nav.timeline'),\"timeline\":_vm.timeline,\"timeline-name\":'friends'}})}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('Timeline',{attrs:{\"title\":_vm.tag,\"timeline\":_vm.timeline,\"timeline-name\":'tag',\"tag\":_vm.tag}})}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('conversation',{attrs:{\"collapsable\":false,\"is-page\":\"true\",\"statusoid\":_vm.statusoid}})}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return (_vm.notification.type === 'mention')?_c('status',{attrs:{\"compact\":true,\"statusoid\":_vm.notification.status}}):_c('div',{staticClass:\"non-mention\",class:[_vm.userClass, { highlighted: _vm.userStyle }],style:([ _vm.userStyle ])},[_c('a',{staticClass:\"avatar-container\",attrs:{\"href\":_vm.notification.from_profile.statusnet_profile_url},on:{\"!click\":function($event){$event.stopPropagation();$event.preventDefault();return _vm.toggleUserExpanded($event)}}},[_c('UserAvatar',{attrs:{\"compact\":true,\"better-shadow\":_vm.betterShadow,\"user\":_vm.notification.from_profile}})],1),_vm._v(\" \"),_c('div',{staticClass:\"notification-right\"},[(_vm.userExpanded)?_c('UserCard',{attrs:{\"user\":_vm.getUser(_vm.notification),\"rounded\":true,\"bordered\":true}}):_vm._e(),_vm._v(\" \"),_c('span',{staticClass:\"notification-details\"},[_c('div',{staticClass:\"name-and-action\"},[(!!_vm.notification.from_profile.name_html)?_c('span',{staticClass:\"username\",attrs:{\"title\":'@'+_vm.notification.from_profile.screen_name},domProps:{\"innerHTML\":_vm._s(_vm.notification.from_profile.name_html)}}):_c('span',{staticClass:\"username\",attrs:{\"title\":'@'+_vm.notification.from_profile.screen_name}},[_vm._v(_vm._s(_vm.notification.from_profile.name))]),_vm._v(\" \"),(_vm.notification.type === 'like')?_c('span',[_c('i',{staticClass:\"fa icon-star lit\"}),_vm._v(\" \"),_c('small',[_vm._v(_vm._s(_vm.$t('notifications.favorited_you')))])]):_vm._e(),_vm._v(\" \"),(_vm.notification.type === 'repeat')?_c('span',[_c('i',{staticClass:\"fa icon-retweet lit\",attrs:{\"title\":_vm.$t('tool_tip.repeat')}}),_vm._v(\" \"),_c('small',[_vm._v(_vm._s(_vm.$t('notifications.repeated_you')))])]):_vm._e(),_vm._v(\" \"),(_vm.notification.type === 'follow')?_c('span',[_c('i',{staticClass:\"fa icon-user-plus lit\"}),_vm._v(\" \"),_c('small',[_vm._v(_vm._s(_vm.$t('notifications.followed_you')))])]):_vm._e()]),_vm._v(\" \"),(_vm.notification.type === 'follow')?_c('div',{staticClass:\"timeago\"},[_c('span',{staticClass:\"faint\"},[_c('Timeago',{attrs:{\"time\":_vm.notification.created_at,\"auto-update\":240}})],1)]):_c('div',{staticClass:\"timeago\"},[(_vm.notification.status)?_c('router-link',{staticClass:\"faint-link\",attrs:{\"to\":{ name: 'conversation', params: { id: _vm.notification.status.id } }}},[_c('Timeago',{attrs:{\"time\":_vm.notification.created_at,\"auto-update\":240}})],1):_vm._e()],1)]),_vm._v(\" \"),(_vm.notification.type === 'follow')?_c('div',{staticClass:\"follow-text\"},[_c('router-link',{attrs:{\"to\":_vm.userProfileLink(_vm.notification.from_profile)}},[_vm._v(\"\\n @\"+_vm._s(_vm.notification.from_profile.screen_name)+\"\\n \")])],1):[_c('status',{staticClass:\"faint\",attrs:{\"compact\":true,\"statusoid\":_vm.notification.action,\"no-heading\":true}})]],2)])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"notifications\",class:{ minimal: _vm.minimalMode }},[_c('div',{class:_vm.mainClass},[(!_vm.noHeading)?_c('div',{staticClass:\"panel-heading\"},[_c('div',{staticClass:\"title\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('notifications.notifications'))+\"\\n \"),(_vm.unseenCount)?_c('span',{staticClass:\"badge badge-notification unseen-count\"},[_vm._v(_vm._s(_vm.unseenCount))]):_vm._e()]),_vm._v(\" \"),(_vm.error)?_c('div',{staticClass:\"loadmore-error alert error\",on:{\"click\":function($event){$event.preventDefault();}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('timeline.error_fetching'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(_vm.unseenCount)?_c('button',{staticClass:\"read-button\",on:{\"click\":function($event){$event.preventDefault();return _vm.markAsSeen($event)}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('notifications.read'))+\"\\n \")]):_vm._e()]):_vm._e(),_vm._v(\" \"),_c('div',{staticClass:\"panel-body\"},_vm._l((_vm.visibleNotifications),function(notification){return _c('div',{key:notification.id,staticClass:\"notification\",class:{\"unseen\": !_vm.minimalMode && !notification.seen}},[_c('div',{staticClass:\"notification-overlay\"}),_vm._v(\" \"),_c('notification',{attrs:{\"notification\":notification}})],1)}),0),_vm._v(\" \"),_c('div',{staticClass:\"panel-footer\"},[(_vm.bottomedOut)?_c('div',{staticClass:\"new-status-notification text-center panel-footer faint\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('notifications.no_more_notifications'))+\"\\n \")]):(!_vm.loading)?_c('a',{attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();_vm.fetchOlderNotifications()}}},[_c('div',{staticClass:\"new-status-notification text-center panel-footer\"},[_vm._v(\"\\n \"+_vm._s(_vm.minimalMode ? _vm.$t('interactions.load_older') : _vm.$t('notifications.load_older'))+\"\\n \")])]):_c('div',{staticClass:\"new-status-notification text-center panel-footer\"},[_c('i',{staticClass:\"icon-spin3 animate-spin\"})])])])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"panel panel-default\"},[_c('div',{staticClass:\"panel-heading\"},[_c('div',{staticClass:\"title\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"nav.interactions\"))+\"\\n \")])]),_vm._v(\" \"),_c('tab-switcher',{ref:\"tabSwitcher\",attrs:{\"on-switch\":_vm.onModeSwitch}},[_c('span',{key:\"mentions\",attrs:{\"label\":_vm.$t('nav.mentions')}}),_vm._v(\" \"),_c('span',{key:\"likes+repeats\",attrs:{\"label\":_vm.$t('interactions.favs_repeats')}}),_vm._v(\" \"),_c('span',{key:\"follows\",attrs:{\"label\":_vm.$t('interactions.follows')}})]),_vm._v(\" \"),_c('Notifications',{ref:\"notifications\",attrs:{\"no-heading\":true,\"minimal-mode\":true,\"filter-mode\":_vm.filterMode}})],1)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('Timeline',{attrs:{\"title\":_vm.$t('nav.dms'),\"timeline\":_vm.timeline,\"timeline-name\":'dms'}})}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"basic-user-card\"},[_c('router-link',{attrs:{\"to\":_vm.userProfileLink(_vm.user)}},[_c('UserAvatar',{staticClass:\"avatar\",attrs:{\"user\":_vm.user},nativeOn:{\"click\":function($event){$event.preventDefault();return _vm.toggleUserExpanded($event)}}})],1),_vm._v(\" \"),(_vm.userExpanded)?_c('div',{staticClass:\"basic-user-card-expanded-content\"},[_c('UserCard',{attrs:{\"user\":_vm.user,\"rounded\":true,\"bordered\":true}})],1):_c('div',{staticClass:\"basic-user-card-collapsed-content\"},[_c('div',{staticClass:\"basic-user-card-user-name\",attrs:{\"title\":_vm.user.name}},[(_vm.user.name_html)?_c('span',{staticClass:\"basic-user-card-user-name-value\",domProps:{\"innerHTML\":_vm._s(_vm.user.name_html)}}):_c('span',{staticClass:\"basic-user-card-user-name-value\"},[_vm._v(_vm._s(_vm.user.name))])]),_vm._v(\" \"),_c('div',[_c('router-link',{staticClass:\"basic-user-card-screen-name\",attrs:{\"to\":_vm.userProfileLink(_vm.user)}},[_vm._v(\"\\n @\"+_vm._s(_vm.user.screen_name)+\"\\n \")])],1),_vm._v(\" \"),_vm._t(\"default\")],2)],1)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('basic-user-card',{attrs:{\"user\":_vm.user}},[_c('div',{staticClass:\"follow-card-content-container\"},[(!_vm.noFollowsYou && _vm.user.follows_you)?_c('span',{staticClass:\"faint\"},[_vm._v(\"\\n \"+_vm._s(_vm.isMe ? _vm.$t('user_card.its_you') : _vm.$t('user_card.follows_you'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(!_vm.loggedIn)?[(!_vm.user.following)?_c('div',{staticClass:\"follow-card-follow-button\"},[_c('RemoteFollow',{attrs:{\"user\":_vm.user}})],1):_vm._e()]:[(!_vm.user.following)?_c('button',{staticClass:\"btn btn-default follow-card-follow-button\",attrs:{\"disabled\":_vm.inProgress,\"title\":_vm.requestSent ? _vm.$t('user_card.follow_again') : ''},on:{\"click\":_vm.followUser}},[(_vm.inProgress)?[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.follow_progress'))+\"\\n \")]:(_vm.requestSent)?[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.follow_sent'))+\"\\n \")]:[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.follow'))+\"\\n \")]],2):_c('button',{staticClass:\"btn btn-default follow-card-follow-button pressed\",attrs:{\"disabled\":_vm.inProgress},on:{\"click\":_vm.unfollowUser}},[(_vm.inProgress)?[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.follow_progress'))+\"\\n \")]:[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.follow_unfollow'))+\"\\n \")]],2)]],2)])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"list\"},[_vm._l((_vm.items),function(item){return _c('div',{key:_vm.getKey(item),staticClass:\"list-item\"},[_vm._t(\"item\",null,{item:item})],2)}),_vm._v(\" \"),(_vm.items.length === 0 && !!_vm.$slots.empty)?_c('div',{staticClass:\"list-empty-content faint\"},[_vm._t(\"empty\")],2):_vm._e()],2)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[(_vm.user)?_c('div',{staticClass:\"user-profile panel panel-default\"},[_c('UserCard',{attrs:{\"user\":_vm.user,\"switcher\":true,\"selected\":_vm.timeline.viewing,\"allow-zooming-avatar\":true,\"rounded\":\"top\"}}),_vm._v(\" \"),_c('tab-switcher',{attrs:{\"active-tab\":_vm.tab,\"render-only-focused\":true,\"on-switch\":_vm.onTabSwitch}},[_c('Timeline',{key:\"statuses\",attrs:{\"label\":_vm.$t('user_card.statuses'),\"count\":_vm.user.statuses_count,\"embedded\":true,\"title\":_vm.$t('user_profile.timeline_title'),\"timeline\":_vm.timeline,\"timeline-name\":\"user\",\"user-id\":_vm.userId,\"pinned-status-ids\":_vm.user.pinnedStatusIds}}),_vm._v(\" \"),(_vm.followsTabVisible)?_c('div',{key:\"followees\",attrs:{\"label\":_vm.$t('user_card.followees'),\"disabled\":!_vm.user.friends_count}},[_c('FriendList',{attrs:{\"user-id\":_vm.userId},scopedSlots:_vm._u([{key:\"item\",fn:function(ref){\nvar item = ref.item;\nreturn [_c('FollowCard',{attrs:{\"user\":item}})]}}])})],1):_vm._e(),_vm._v(\" \"),(_vm.followersTabVisible)?_c('div',{key:\"followers\",attrs:{\"label\":_vm.$t('user_card.followers'),\"disabled\":!_vm.user.followers_count}},[_c('FollowerList',{attrs:{\"user-id\":_vm.userId},scopedSlots:_vm._u([{key:\"item\",fn:function(ref){\nvar item = ref.item;\nreturn [_c('FollowCard',{attrs:{\"user\":item,\"no-follows-you\":_vm.isUs}})]}}])})],1):_vm._e(),_vm._v(\" \"),_c('Timeline',{key:\"media\",attrs:{\"label\":_vm.$t('user_card.media'),\"disabled\":!_vm.media.visibleStatuses.length,\"embedded\":true,\"title\":_vm.$t('user_card.media'),\"timeline-name\":\"media\",\"timeline\":_vm.media,\"user-id\":_vm.userId}}),_vm._v(\" \"),(_vm.isUs)?_c('Timeline',{key:\"favorites\",attrs:{\"label\":_vm.$t('user_card.favorites'),\"disabled\":!_vm.favorites.visibleStatuses.length,\"embedded\":true,\"title\":_vm.$t('user_card.favorites'),\"timeline-name\":\"favorites\",\"timeline\":_vm.favorites}}):_vm._e()],1)],1):_c('div',{staticClass:\"panel user-profile-placeholder\"},[_c('div',{staticClass:\"panel-heading\"},[_c('div',{staticClass:\"title\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.profile_tab'))+\"\\n \")])]),_vm._v(\" \"),_c('div',{staticClass:\"panel-body\"},[(_vm.error)?_c('span',[_vm._v(_vm._s(_vm.error))]):_c('i',{staticClass:\"icon-spin3 animate-spin\"})])])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"panel panel-default\"},[_c('div',{staticClass:\"panel-heading\"},[_c('div',{staticClass:\"title\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('nav.search'))+\"\\n \")])]),_vm._v(\" \"),_c('div',{staticClass:\"search-input-container\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.searchTerm),expression:\"searchTerm\"}],ref:\"searchInput\",staticClass:\"search-input\",attrs:{\"placeholder\":_vm.$t('nav.search')},domProps:{\"value\":(_vm.searchTerm)},on:{\"keyup\":function($event){if(!('button' in $event)&&_vm._k($event.keyCode,\"enter\",13,$event.key,\"Enter\")){ return null; }_vm.newQuery(_vm.searchTerm)},\"input\":function($event){if($event.target.composing){ return; }_vm.searchTerm=$event.target.value}}}),_vm._v(\" \"),_c('button',{staticClass:\"btn search-button\",on:{\"click\":function($event){_vm.newQuery(_vm.searchTerm)}}},[_c('i',{staticClass:\"icon-search\"})])]),_vm._v(\" \"),(_vm.loading)?_c('div',{staticClass:\"text-center loading-icon\"},[_c('i',{staticClass:\"icon-spin3 animate-spin\"})]):(_vm.loaded)?_c('div',[_c('div',{staticClass:\"search-nav-heading\"},[_c('tab-switcher',{ref:\"tabSwitcher\",attrs:{\"on-switch\":_vm.onResultTabSwitch,\"active-tab\":_vm.currenResultTab}},[_c('span',{key:\"statuses\",attrs:{\"label\":_vm.$t('user_card.statuses') + _vm.resultCount('visibleStatuses')}}),_vm._v(\" \"),_c('span',{key:\"people\",attrs:{\"label\":_vm.$t('search.people') + _vm.resultCount('users')}}),_vm._v(\" \"),_c('span',{key:\"hashtags\",attrs:{\"label\":_vm.$t('search.hashtags') + _vm.resultCount('hashtags')}})])],1)]):_vm._e(),_vm._v(\" \"),_c('div',{staticClass:\"panel-body\"},[(_vm.currenResultTab === 'statuses')?_c('div',[(_vm.visibleStatuses.length === 0 && !_vm.loading && _vm.loaded)?_c('div',{staticClass:\"search-result-heading\"},[_c('h4',[_vm._v(_vm._s(_vm.$t('search.no_results')))])]):_vm._e(),_vm._v(\" \"),_vm._l((_vm.visibleStatuses),function(status){return _c('Status',{key:status.id,staticClass:\"search-result\",attrs:{\"collapsable\":false,\"expandable\":false,\"compact\":false,\"statusoid\":status,\"no-heading\":false}})})],2):(_vm.currenResultTab === 'people')?_c('div',[(_vm.users.length === 0 && !_vm.loading && _vm.loaded)?_c('div',{staticClass:\"search-result-heading\"},[_c('h4',[_vm._v(_vm._s(_vm.$t('search.no_results')))])]):_vm._e(),_vm._v(\" \"),_vm._l((_vm.users),function(user){return _c('FollowCard',{key:user.id,staticClass:\"list-item search-result\",attrs:{\"user\":user}})})],2):(_vm.currenResultTab === 'hashtags')?_c('div',[(_vm.hashtags.length === 0 && !_vm.loading && _vm.loaded)?_c('div',{staticClass:\"search-result-heading\"},[_c('h4',[_vm._v(_vm._s(_vm.$t('search.no_results')))])]):_vm._e(),_vm._v(\" \"),_vm._l((_vm.hashtags),function(hashtag){return _c('div',{key:hashtag.url,staticClass:\"status trend search-result\"},[_c('div',{staticClass:\"hashtag\"},[_c('router-link',{attrs:{\"to\":{ name: 'tag-timeline', params: { tag: hashtag.name } }}},[_vm._v(\"\\n #\"+_vm._s(hashtag.name)+\"\\n \")]),_vm._v(\" \"),(_vm.lastHistoryRecord(hashtag))?_c('div',[(_vm.lastHistoryRecord(hashtag).accounts == 1)?_c('span',[_vm._v(\"\\n \"+_vm._s(_vm.$t('search.person_talking', { count: _vm.lastHistoryRecord(hashtag).accounts }))+\"\\n \")]):_c('span',[_vm._v(\"\\n \"+_vm._s(_vm.$t('search.people_talking', { count: _vm.lastHistoryRecord(hashtag).accounts }))+\"\\n \")])]):_vm._e()],1),_vm._v(\" \"),(_vm.lastHistoryRecord(hashtag))?_c('div',{staticClass:\"count\"},[_vm._v(\"\\n \"+_vm._s(_vm.lastHistoryRecord(hashtag).uses)+\"\\n \")]):_vm._e()])})],2):_vm._e()]),_vm._v(\" \"),_c('div',{staticClass:\"search-result-footer text-center panel-footer faint\"})])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"color-control style-control\",class:{ disabled: !_vm.present || _vm.disabled }},[_c('label',{staticClass:\"label\",attrs:{\"for\":_vm.name}},[_vm._v(\"\\n \"+_vm._s(_vm.label)+\"\\n \")]),_vm._v(\" \"),(typeof _vm.fallback !== 'undefined')?_c('input',{staticClass:\"opt exlcude-disabled\",attrs:{\"id\":_vm.name + '-o',\"type\":\"checkbox\"},domProps:{\"checked\":_vm.present},on:{\"input\":function($event){_vm.$emit('input', typeof _vm.value === 'undefined' ? _vm.fallback : undefined)}}}):_vm._e(),_vm._v(\" \"),(typeof _vm.fallback !== 'undefined')?_c('label',{staticClass:\"opt-l\",attrs:{\"for\":_vm.name + '-o'}}):_vm._e(),_vm._v(\" \"),_c('input',{staticClass:\"color-input\",attrs:{\"id\":_vm.name,\"type\":\"color\",\"disabled\":!_vm.present || _vm.disabled},domProps:{\"value\":_vm.value || _vm.fallback},on:{\"input\":function($event){_vm.$emit('input', $event.target.value)}}}),_vm._v(\" \"),_c('input',{staticClass:\"text-input\",attrs:{\"id\":_vm.name + '-t',\"type\":\"text\",\"disabled\":!_vm.present || _vm.disabled},domProps:{\"value\":_vm.value || _vm.fallback},on:{\"input\":function($event){_vm.$emit('input', $event.target.value)}}})])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"range-control style-control\",class:{ disabled: !_vm.present || _vm.disabled }},[_c('label',{staticClass:\"label\",attrs:{\"for\":_vm.name}},[_vm._v(\"\\n \"+_vm._s(_vm.label)+\"\\n \")]),_vm._v(\" \"),(typeof _vm.fallback !== 'undefined')?_c('input',{staticClass:\"opt exclude-disabled\",attrs:{\"id\":_vm.name + '-o',\"type\":\"checkbox\"},domProps:{\"checked\":_vm.present},on:{\"input\":function($event){_vm.$emit('input', !_vm.present ? _vm.fallback : undefined)}}}):_vm._e(),_vm._v(\" \"),(typeof _vm.fallback !== 'undefined')?_c('label',{staticClass:\"opt-l\",attrs:{\"for\":_vm.name + '-o'}}):_vm._e(),_vm._v(\" \"),_c('input',{staticClass:\"input-number\",attrs:{\"id\":_vm.name,\"type\":\"range\",\"disabled\":!_vm.present || _vm.disabled,\"max\":_vm.max || _vm.hardMax || 100,\"min\":_vm.min || _vm.hardMin || 0,\"step\":_vm.step || 1},domProps:{\"value\":_vm.value || _vm.fallback},on:{\"input\":function($event){_vm.$emit('input', $event.target.value)}}}),_vm._v(\" \"),_c('input',{staticClass:\"input-number\",attrs:{\"id\":_vm.name,\"type\":\"number\",\"disabled\":!_vm.present || _vm.disabled,\"max\":_vm.hardMax,\"min\":_vm.hardMin,\"step\":_vm.step || 1},domProps:{\"value\":_vm.value || _vm.fallback},on:{\"input\":function($event){_vm.$emit('input', $event.target.value)}}})])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"opacity-control style-control\",class:{ disabled: !_vm.present || _vm.disabled }},[_c('label',{staticClass:\"label\",attrs:{\"for\":_vm.name}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.style.common.opacity'))+\"\\n \")]),_vm._v(\" \"),(typeof _vm.fallback !== 'undefined')?_c('input',{staticClass:\"opt exclude-disabled\",attrs:{\"id\":_vm.name + '-o',\"type\":\"checkbox\"},domProps:{\"checked\":_vm.present},on:{\"input\":function($event){_vm.$emit('input', !_vm.present ? _vm.fallback : undefined)}}}):_vm._e(),_vm._v(\" \"),(typeof _vm.fallback !== 'undefined')?_c('label',{staticClass:\"opt-l\",attrs:{\"for\":_vm.name + '-o'}}):_vm._e(),_vm._v(\" \"),_c('input',{staticClass:\"input-number\",attrs:{\"id\":_vm.name,\"type\":\"number\",\"disabled\":!_vm.present || _vm.disabled,\"max\":\"1\",\"min\":\"0\",\"step\":\".05\"},domProps:{\"value\":_vm.value || _vm.fallback},on:{\"input\":function($event){_vm.$emit('input', $event.target.value)}}})])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"shadow-control\",class:{ disabled: !_vm.present }},[_c('div',{staticClass:\"shadow-preview-container\"},[_c('div',{staticClass:\"y-shift-control\",attrs:{\"disabled\":!_vm.present}},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.selected.y),expression:\"selected.y\"}],staticClass:\"input-number\",attrs:{\"disabled\":!_vm.present,\"type\":\"number\"},domProps:{\"value\":(_vm.selected.y)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.selected, \"y\", $event.target.value)}}}),_vm._v(\" \"),_c('div',{staticClass:\"wrap\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.selected.y),expression:\"selected.y\"}],staticClass:\"input-range\",attrs:{\"disabled\":!_vm.present,\"type\":\"range\",\"max\":\"20\",\"min\":\"-20\"},domProps:{\"value\":(_vm.selected.y)},on:{\"__r\":function($event){_vm.$set(_vm.selected, \"y\", $event.target.value)}}})])]),_vm._v(\" \"),_c('div',{staticClass:\"preview-window\"},[_c('div',{staticClass:\"preview-block\",style:(_vm.style)})]),_vm._v(\" \"),_c('div',{staticClass:\"x-shift-control\",attrs:{\"disabled\":!_vm.present}},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.selected.x),expression:\"selected.x\"}],staticClass:\"input-number\",attrs:{\"disabled\":!_vm.present,\"type\":\"number\"},domProps:{\"value\":(_vm.selected.x)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.selected, \"x\", $event.target.value)}}}),_vm._v(\" \"),_c('div',{staticClass:\"wrap\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.selected.x),expression:\"selected.x\"}],staticClass:\"input-range\",attrs:{\"disabled\":!_vm.present,\"type\":\"range\",\"max\":\"20\",\"min\":\"-20\"},domProps:{\"value\":(_vm.selected.x)},on:{\"__r\":function($event){_vm.$set(_vm.selected, \"x\", $event.target.value)}}})])])]),_vm._v(\" \"),_c('div',{staticClass:\"shadow-tweak\"},[_c('div',{staticClass:\"id-control style-control\",attrs:{\"disabled\":_vm.usingFallback}},[_c('label',{staticClass:\"select\",attrs:{\"for\":\"shadow-switcher\",\"disabled\":!_vm.ready || _vm.usingFallback}},[_c('select',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.selectedId),expression:\"selectedId\"}],staticClass:\"shadow-switcher\",attrs:{\"id\":\"shadow-switcher\",\"disabled\":!_vm.ready || _vm.usingFallback},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.selectedId=$event.target.multiple ? $$selectedVal : $$selectedVal[0]}}},_vm._l((_vm.cValue),function(shadow,index){return _c('option',{key:index,domProps:{\"value\":index}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.style.shadows.shadow_id', { value: index }))+\"\\n \")])}),0),_vm._v(\" \"),_c('i',{staticClass:\"icon-down-open\"})]),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-default\",attrs:{\"disabled\":!_vm.ready || !_vm.present},on:{\"click\":_vm.del}},[_c('i',{staticClass:\"icon-cancel\"})]),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-default\",attrs:{\"disabled\":!_vm.moveUpValid},on:{\"click\":_vm.moveUp}},[_c('i',{staticClass:\"icon-up-open\"})]),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-default\",attrs:{\"disabled\":!_vm.moveDnValid},on:{\"click\":_vm.moveDn}},[_c('i',{staticClass:\"icon-down-open\"})]),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-default\",attrs:{\"disabled\":_vm.usingFallback},on:{\"click\":_vm.add}},[_c('i',{staticClass:\"icon-plus\"})])]),_vm._v(\" \"),_c('div',{staticClass:\"inset-control style-control\",attrs:{\"disabled\":!_vm.present}},[_c('label',{staticClass:\"label\",attrs:{\"for\":\"inset\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.style.shadows.inset'))+\"\\n \")]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.selected.inset),expression:\"selected.inset\"}],staticClass:\"input-inset\",attrs:{\"id\":\"inset\",\"disabled\":!_vm.present,\"name\":\"inset\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.selected.inset)?_vm._i(_vm.selected.inset,null)>-1:(_vm.selected.inset)},on:{\"change\":function($event){var $$a=_vm.selected.inset,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.$set(_vm.selected, \"inset\", $$a.concat([$$v])))}else{$$i>-1&&(_vm.$set(_vm.selected, \"inset\", $$a.slice(0,$$i).concat($$a.slice($$i+1))))}}else{_vm.$set(_vm.selected, \"inset\", $$c)}}}}),_vm._v(\" \"),_c('label',{staticClass:\"checkbox-label\",attrs:{\"for\":\"inset\"}})]),_vm._v(\" \"),_c('div',{staticClass:\"blur-control style-control\",attrs:{\"disabled\":!_vm.present}},[_c('label',{staticClass:\"label\",attrs:{\"for\":\"spread\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.style.shadows.blur'))+\"\\n \")]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.selected.blur),expression:\"selected.blur\"}],staticClass:\"input-range\",attrs:{\"id\":\"blur\",\"disabled\":!_vm.present,\"name\":\"blur\",\"type\":\"range\",\"max\":\"20\",\"min\":\"0\"},domProps:{\"value\":(_vm.selected.blur)},on:{\"__r\":function($event){_vm.$set(_vm.selected, \"blur\", $event.target.value)}}}),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.selected.blur),expression:\"selected.blur\"}],staticClass:\"input-number\",attrs:{\"disabled\":!_vm.present,\"type\":\"number\",\"min\":\"0\"},domProps:{\"value\":(_vm.selected.blur)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.selected, \"blur\", $event.target.value)}}})]),_vm._v(\" \"),_c('div',{staticClass:\"spread-control style-control\",attrs:{\"disabled\":!_vm.present}},[_c('label',{staticClass:\"label\",attrs:{\"for\":\"spread\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.style.shadows.spread'))+\"\\n \")]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.selected.spread),expression:\"selected.spread\"}],staticClass:\"input-range\",attrs:{\"id\":\"spread\",\"disabled\":!_vm.present,\"name\":\"spread\",\"type\":\"range\",\"max\":\"20\",\"min\":\"-20\"},domProps:{\"value\":(_vm.selected.spread)},on:{\"__r\":function($event){_vm.$set(_vm.selected, \"spread\", $event.target.value)}}}),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.selected.spread),expression:\"selected.spread\"}],staticClass:\"input-number\",attrs:{\"disabled\":!_vm.present,\"type\":\"number\"},domProps:{\"value\":(_vm.selected.spread)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.selected, \"spread\", $event.target.value)}}})]),_vm._v(\" \"),_c('ColorInput',{attrs:{\"disabled\":!_vm.present,\"label\":_vm.$t('settings.style.common.color'),\"name\":\"shadow\"},model:{value:(_vm.selected.color),callback:function ($$v) {_vm.$set(_vm.selected, \"color\", $$v)},expression:\"selected.color\"}}),_vm._v(\" \"),_c('OpacityInput',{attrs:{\"disabled\":!_vm.present},model:{value:(_vm.selected.alpha),callback:function ($$v) {_vm.$set(_vm.selected, \"alpha\", $$v)},expression:\"selected.alpha\"}}),_vm._v(\" \"),_c('p',[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.style.shadows.hint'))+\"\\n \")])],1)])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"font-control style-control\",class:{ custom: _vm.isCustom }},[_c('label',{staticClass:\"label\",attrs:{\"for\":_vm.preset === 'custom' ? _vm.name : _vm.name + '-font-switcher'}},[_vm._v(\"\\n \"+_vm._s(_vm.label)+\"\\n \")]),_vm._v(\" \"),(typeof _vm.fallback !== 'undefined')?_c('input',{staticClass:\"opt exlcude-disabled\",attrs:{\"id\":_vm.name + '-o',\"type\":\"checkbox\"},domProps:{\"checked\":_vm.present},on:{\"input\":function($event){_vm.$emit('input', typeof _vm.value === 'undefined' ? _vm.fallback : undefined)}}}):_vm._e(),_vm._v(\" \"),(typeof _vm.fallback !== 'undefined')?_c('label',{staticClass:\"opt-l\",attrs:{\"for\":_vm.name + '-o'}}):_vm._e(),_vm._v(\" \"),_c('label',{staticClass:\"select\",attrs:{\"for\":_vm.name + '-font-switcher',\"disabled\":!_vm.present}},[_c('select',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.preset),expression:\"preset\"}],staticClass:\"font-switcher\",attrs:{\"id\":_vm.name + '-font-switcher',\"disabled\":!_vm.present},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.preset=$event.target.multiple ? $$selectedVal : $$selectedVal[0]}}},_vm._l((_vm.availableOptions),function(option){return _c('option',{key:option,domProps:{\"value\":option}},[_vm._v(\"\\n \"+_vm._s(option === 'custom' ? _vm.$t('settings.style.fonts.custom') : option)+\"\\n \")])}),0),_vm._v(\" \"),_c('i',{staticClass:\"icon-down-open\"})]),_vm._v(\" \"),(_vm.isCustom)?_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.family),expression:\"family\"}],staticClass:\"custom-font\",attrs:{\"id\":_vm.name,\"type\":\"text\"},domProps:{\"value\":(_vm.family)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.family=$event.target.value}}}):_vm._e()])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return (_vm.contrast)?_c('span',{staticClass:\"contrast-ratio\"},[_c('span',{staticClass:\"rating\",attrs:{\"title\":_vm.hint}},[(_vm.contrast.aaa)?_c('span',[_c('i',{staticClass:\"icon-thumbs-up-alt\"})]):_vm._e(),_vm._v(\" \"),(!_vm.contrast.aaa && _vm.contrast.aa)?_c('span',[_c('i',{staticClass:\"icon-adjust\"})]):_vm._e(),_vm._v(\" \"),(!_vm.contrast.aaa && !_vm.contrast.aa)?_c('span',[_c('i',{staticClass:\"icon-attention\"})]):_vm._e()]),_vm._v(\" \"),(_vm.contrast && _vm.large)?_c('span',{staticClass:\"rating\",attrs:{\"title\":_vm.hint_18pt}},[(_vm.contrast.laaa)?_c('span',[_c('i',{staticClass:\"icon-thumbs-up-alt\"})]):_vm._e(),_vm._v(\" \"),(!_vm.contrast.laaa && _vm.contrast.laa)?_c('span',[_c('i',{staticClass:\"icon-adjust\"})]):_vm._e(),_vm._v(\" \"),(!_vm.contrast.laaa && !_vm.contrast.laa)?_c('span',[_c('i',{staticClass:\"icon-attention\"})]):_vm._e()]):_vm._e()]):_vm._e()}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"import-export-container\"},[_vm._t(\"before\"),_vm._v(\" \"),_c('button',{staticClass:\"btn\",on:{\"click\":_vm.exportData}},[_vm._v(\"\\n \"+_vm._s(_vm.exportLabel)+\"\\n \")]),_vm._v(\" \"),_c('button',{staticClass:\"btn\",on:{\"click\":_vm.importData}},[_vm._v(\"\\n \"+_vm._s(_vm.importLabel)+\"\\n \")]),_vm._v(\" \"),_vm._t(\"afterButtons\"),_vm._v(\" \"),(_vm.importFailed)?_c('p',{staticClass:\"alert error\"},[_vm._v(\"\\n \"+_vm._s(_vm.importFailedText)+\"\\n \")]):_vm._e(),_vm._v(\" \"),_vm._t(\"afterError\")],2)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"style-switcher\"},[_c('div',{staticClass:\"presets-container\"},[_c('div',{staticClass:\"save-load\"},[_c('export-import',{attrs:{\"export-object\":_vm.exportedTheme,\"export-label\":_vm.$t(\"settings.export_theme\"),\"import-label\":_vm.$t(\"settings.import_theme\"),\"import-failed-text\":_vm.$t(\"settings.invalid_theme_imported\"),\"on-import\":_vm.onImport,\"validator\":_vm.importValidator}},[_c('template',{slot:\"before\"},[_c('div',{staticClass:\"presets\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.presets'))+\"\\n \"),_c('label',{staticClass:\"select\",attrs:{\"for\":\"preset-switcher\"}},[_c('select',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.selected),expression:\"selected\"}],staticClass:\"preset-switcher\",attrs:{\"id\":\"preset-switcher\"},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.selected=$event.target.multiple ? $$selectedVal : $$selectedVal[0]}}},_vm._l((_vm.availableStyles),function(style){return _c('option',{key:style.name,style:({\n backgroundColor: style[1] || style.theme.colors.bg,\n color: style[3] || style.theme.colors.text\n }),domProps:{\"value\":style}},[_vm._v(\"\\n \"+_vm._s(style[0] || style.name)+\"\\n \")])}),0),_vm._v(\" \"),_c('i',{staticClass:\"icon-down-open\"})])])])],2)],1),_vm._v(\" \"),_c('div',{staticClass:\"save-load-options\"},[_c('span',{staticClass:\"keep-option\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.keepColor),expression:\"keepColor\"}],attrs:{\"id\":\"keep-color\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.keepColor)?_vm._i(_vm.keepColor,null)>-1:(_vm.keepColor)},on:{\"change\":function($event){var $$a=_vm.keepColor,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.keepColor=$$a.concat([$$v]))}else{$$i>-1&&(_vm.keepColor=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.keepColor=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"keep-color\"}},[_vm._v(_vm._s(_vm.$t('settings.style.switcher.keep_color')))])]),_vm._v(\" \"),_c('span',{staticClass:\"keep-option\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.keepShadows),expression:\"keepShadows\"}],attrs:{\"id\":\"keep-shadows\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.keepShadows)?_vm._i(_vm.keepShadows,null)>-1:(_vm.keepShadows)},on:{\"change\":function($event){var $$a=_vm.keepShadows,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.keepShadows=$$a.concat([$$v]))}else{$$i>-1&&(_vm.keepShadows=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.keepShadows=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"keep-shadows\"}},[_vm._v(_vm._s(_vm.$t('settings.style.switcher.keep_shadows')))])]),_vm._v(\" \"),_c('span',{staticClass:\"keep-option\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.keepOpacity),expression:\"keepOpacity\"}],attrs:{\"id\":\"keep-opacity\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.keepOpacity)?_vm._i(_vm.keepOpacity,null)>-1:(_vm.keepOpacity)},on:{\"change\":function($event){var $$a=_vm.keepOpacity,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.keepOpacity=$$a.concat([$$v]))}else{$$i>-1&&(_vm.keepOpacity=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.keepOpacity=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"keep-opacity\"}},[_vm._v(_vm._s(_vm.$t('settings.style.switcher.keep_opacity')))])]),_vm._v(\" \"),_c('span',{staticClass:\"keep-option\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.keepRoundness),expression:\"keepRoundness\"}],attrs:{\"id\":\"keep-roundness\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.keepRoundness)?_vm._i(_vm.keepRoundness,null)>-1:(_vm.keepRoundness)},on:{\"change\":function($event){var $$a=_vm.keepRoundness,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.keepRoundness=$$a.concat([$$v]))}else{$$i>-1&&(_vm.keepRoundness=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.keepRoundness=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"keep-roundness\"}},[_vm._v(_vm._s(_vm.$t('settings.style.switcher.keep_roundness')))])]),_vm._v(\" \"),_c('span',{staticClass:\"keep-option\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.keepFonts),expression:\"keepFonts\"}],attrs:{\"id\":\"keep-fonts\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.keepFonts)?_vm._i(_vm.keepFonts,null)>-1:(_vm.keepFonts)},on:{\"change\":function($event){var $$a=_vm.keepFonts,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.keepFonts=$$a.concat([$$v]))}else{$$i>-1&&(_vm.keepFonts=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.keepFonts=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"keep-fonts\"}},[_vm._v(_vm._s(_vm.$t('settings.style.switcher.keep_fonts')))])]),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.style.switcher.save_load_hint')))])])]),_vm._v(\" \"),_c('div',{staticClass:\"preview-container\"},[_c('preview',{style:(_vm.previewRules)})],1),_vm._v(\" \"),_c('keep-alive',[_c('tab-switcher',{key:\"style-tweak\"},[_c('div',{staticClass:\"color-container\",attrs:{\"label\":_vm.$t('settings.style.common_colors._tab_label')}},[_c('div',{staticClass:\"tab-header\"},[_c('p',[_vm._v(_vm._s(_vm.$t('settings.theme_help')))]),_vm._v(\" \"),_c('button',{staticClass:\"btn\",on:{\"click\":_vm.clearOpacity}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.style.switcher.clear_opacity'))+\"\\n \")]),_vm._v(\" \"),_c('button',{staticClass:\"btn\",on:{\"click\":_vm.clearV1}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.style.switcher.clear_all'))+\"\\n \")])]),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.theme_help_v2_1')))]),_vm._v(\" \"),_c('h4',[_vm._v(_vm._s(_vm.$t('settings.style.common_colors.main')))]),_vm._v(\" \"),_c('div',{staticClass:\"color-item\"},[_c('ColorInput',{attrs:{\"name\":\"bgColor\",\"label\":_vm.$t('settings.background')},model:{value:(_vm.bgColorLocal),callback:function ($$v) {_vm.bgColorLocal=$$v},expression:\"bgColorLocal\"}}),_vm._v(\" \"),_c('OpacityInput',{attrs:{\"name\":\"bgOpacity\",\"fallback\":_vm.previewTheme.opacity.bg || 1},model:{value:(_vm.bgOpacityLocal),callback:function ($$v) {_vm.bgOpacityLocal=$$v},expression:\"bgOpacityLocal\"}}),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"textColor\",\"label\":_vm.$t('settings.text')},model:{value:(_vm.textColorLocal),callback:function ($$v) {_vm.textColorLocal=$$v},expression:\"textColorLocal\"}}),_vm._v(\" \"),_c('ContrastRatio',{attrs:{\"contrast\":_vm.previewContrast.bgText}}),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"linkColor\",\"label\":_vm.$t('settings.links')},model:{value:(_vm.linkColorLocal),callback:function ($$v) {_vm.linkColorLocal=$$v},expression:\"linkColorLocal\"}}),_vm._v(\" \"),_c('ContrastRatio',{attrs:{\"contrast\":_vm.previewContrast.bgLink}})],1),_vm._v(\" \"),_c('div',{staticClass:\"color-item\"},[_c('ColorInput',{attrs:{\"name\":\"fgColor\",\"label\":_vm.$t('settings.foreground')},model:{value:(_vm.fgColorLocal),callback:function ($$v) {_vm.fgColorLocal=$$v},expression:\"fgColorLocal\"}}),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"fgTextColor\",\"label\":_vm.$t('settings.text'),\"fallback\":_vm.previewTheme.colors.fgText},model:{value:(_vm.fgTextColorLocal),callback:function ($$v) {_vm.fgTextColorLocal=$$v},expression:\"fgTextColorLocal\"}}),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"fgLinkColor\",\"label\":_vm.$t('settings.links'),\"fallback\":_vm.previewTheme.colors.fgLink},model:{value:(_vm.fgLinkColorLocal),callback:function ($$v) {_vm.fgLinkColorLocal=$$v},expression:\"fgLinkColorLocal\"}}),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.style.common_colors.foreground_hint')))])],1),_vm._v(\" \"),_c('h4',[_vm._v(_vm._s(_vm.$t('settings.style.common_colors.rgbo')))]),_vm._v(\" \"),_c('div',{staticClass:\"color-item\"},[_c('ColorInput',{attrs:{\"name\":\"cRedColor\",\"label\":_vm.$t('settings.cRed')},model:{value:(_vm.cRedColorLocal),callback:function ($$v) {_vm.cRedColorLocal=$$v},expression:\"cRedColorLocal\"}}),_vm._v(\" \"),_c('ContrastRatio',{attrs:{\"contrast\":_vm.previewContrast.bgRed}}),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"cBlueColor\",\"label\":_vm.$t('settings.cBlue')},model:{value:(_vm.cBlueColorLocal),callback:function ($$v) {_vm.cBlueColorLocal=$$v},expression:\"cBlueColorLocal\"}}),_vm._v(\" \"),_c('ContrastRatio',{attrs:{\"contrast\":_vm.previewContrast.bgBlue}})],1),_vm._v(\" \"),_c('div',{staticClass:\"color-item\"},[_c('ColorInput',{attrs:{\"name\":\"cGreenColor\",\"label\":_vm.$t('settings.cGreen')},model:{value:(_vm.cGreenColorLocal),callback:function ($$v) {_vm.cGreenColorLocal=$$v},expression:\"cGreenColorLocal\"}}),_vm._v(\" \"),_c('ContrastRatio',{attrs:{\"contrast\":_vm.previewContrast.bgGreen}}),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"cOrangeColor\",\"label\":_vm.$t('settings.cOrange')},model:{value:(_vm.cOrangeColorLocal),callback:function ($$v) {_vm.cOrangeColorLocal=$$v},expression:\"cOrangeColorLocal\"}}),_vm._v(\" \"),_c('ContrastRatio',{attrs:{\"contrast\":_vm.previewContrast.bgOrange}})],1),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.theme_help_v2_2')))])]),_vm._v(\" \"),_c('div',{staticClass:\"color-container\",attrs:{\"label\":_vm.$t('settings.style.advanced_colors._tab_label')}},[_c('div',{staticClass:\"tab-header\"},[_c('p',[_vm._v(_vm._s(_vm.$t('settings.theme_help')))]),_vm._v(\" \"),_c('button',{staticClass:\"btn\",on:{\"click\":_vm.clearOpacity}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.style.switcher.clear_opacity'))+\"\\n \")]),_vm._v(\" \"),_c('button',{staticClass:\"btn\",on:{\"click\":_vm.clearV1}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.style.switcher.clear_all'))+\"\\n \")])]),_vm._v(\" \"),_c('div',{staticClass:\"color-item\"},[_c('h4',[_vm._v(_vm._s(_vm.$t('settings.style.advanced_colors.alert')))]),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"alertError\",\"label\":_vm.$t('settings.style.advanced_colors.alert_error'),\"fallback\":_vm.previewTheme.colors.alertError},model:{value:(_vm.alertErrorColorLocal),callback:function ($$v) {_vm.alertErrorColorLocal=$$v},expression:\"alertErrorColorLocal\"}}),_vm._v(\" \"),_c('ContrastRatio',{attrs:{\"contrast\":_vm.previewContrast.alertError}})],1),_vm._v(\" \"),_c('div',{staticClass:\"color-item\"},[_c('h4',[_vm._v(_vm._s(_vm.$t('settings.style.advanced_colors.badge')))]),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"badgeNotification\",\"label\":_vm.$t('settings.style.advanced_colors.badge_notification'),\"fallback\":_vm.previewTheme.colors.badgeNotification},model:{value:(_vm.badgeNotificationColorLocal),callback:function ($$v) {_vm.badgeNotificationColorLocal=$$v},expression:\"badgeNotificationColorLocal\"}})],1),_vm._v(\" \"),_c('div',{staticClass:\"color-item\"},[_c('h4',[_vm._v(_vm._s(_vm.$t('settings.style.advanced_colors.panel_header')))]),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"panelColor\",\"fallback\":_vm.fgColorLocal,\"label\":_vm.$t('settings.background')},model:{value:(_vm.panelColorLocal),callback:function ($$v) {_vm.panelColorLocal=$$v},expression:\"panelColorLocal\"}}),_vm._v(\" \"),_c('OpacityInput',{attrs:{\"name\":\"panelOpacity\",\"fallback\":_vm.previewTheme.opacity.panel || 1},model:{value:(_vm.panelOpacityLocal),callback:function ($$v) {_vm.panelOpacityLocal=$$v},expression:\"panelOpacityLocal\"}}),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"panelTextColor\",\"fallback\":_vm.previewTheme.colors.panelText,\"label\":_vm.$t('settings.text')},model:{value:(_vm.panelTextColorLocal),callback:function ($$v) {_vm.panelTextColorLocal=$$v},expression:\"panelTextColorLocal\"}}),_vm._v(\" \"),_c('ContrastRatio',{attrs:{\"contrast\":_vm.previewContrast.panelText,\"large\":\"1\"}}),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"panelLinkColor\",\"fallback\":_vm.previewTheme.colors.panelLink,\"label\":_vm.$t('settings.links')},model:{value:(_vm.panelLinkColorLocal),callback:function ($$v) {_vm.panelLinkColorLocal=$$v},expression:\"panelLinkColorLocal\"}}),_vm._v(\" \"),_c('ContrastRatio',{attrs:{\"contrast\":_vm.previewContrast.panelLink,\"large\":\"1\"}})],1),_vm._v(\" \"),_c('div',{staticClass:\"color-item\"},[_c('h4',[_vm._v(_vm._s(_vm.$t('settings.style.advanced_colors.top_bar')))]),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"topBarColor\",\"fallback\":_vm.fgColorLocal,\"label\":_vm.$t('settings.background')},model:{value:(_vm.topBarColorLocal),callback:function ($$v) {_vm.topBarColorLocal=$$v},expression:\"topBarColorLocal\"}}),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"topBarTextColor\",\"fallback\":_vm.previewTheme.colors.topBarText,\"label\":_vm.$t('settings.text')},model:{value:(_vm.topBarTextColorLocal),callback:function ($$v) {_vm.topBarTextColorLocal=$$v},expression:\"topBarTextColorLocal\"}}),_vm._v(\" \"),_c('ContrastRatio',{attrs:{\"contrast\":_vm.previewContrast.topBarText}}),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"topBarLinkColor\",\"fallback\":_vm.previewTheme.colors.topBarLink,\"label\":_vm.$t('settings.links')},model:{value:(_vm.topBarLinkColorLocal),callback:function ($$v) {_vm.topBarLinkColorLocal=$$v},expression:\"topBarLinkColorLocal\"}}),_vm._v(\" \"),_c('ContrastRatio',{attrs:{\"contrast\":_vm.previewContrast.topBarLink}})],1),_vm._v(\" \"),_c('div',{staticClass:\"color-item\"},[_c('h4',[_vm._v(_vm._s(_vm.$t('settings.style.advanced_colors.inputs')))]),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"inputColor\",\"fallback\":_vm.fgColorLocal,\"label\":_vm.$t('settings.background')},model:{value:(_vm.inputColorLocal),callback:function ($$v) {_vm.inputColorLocal=$$v},expression:\"inputColorLocal\"}}),_vm._v(\" \"),_c('OpacityInput',{attrs:{\"name\":\"inputOpacity\",\"fallback\":_vm.previewTheme.opacity.input || 1},model:{value:(_vm.inputOpacityLocal),callback:function ($$v) {_vm.inputOpacityLocal=$$v},expression:\"inputOpacityLocal\"}}),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"inputTextColor\",\"fallback\":_vm.previewTheme.colors.inputText,\"label\":_vm.$t('settings.text')},model:{value:(_vm.inputTextColorLocal),callback:function ($$v) {_vm.inputTextColorLocal=$$v},expression:\"inputTextColorLocal\"}}),_vm._v(\" \"),_c('ContrastRatio',{attrs:{\"contrast\":_vm.previewContrast.inputText}})],1),_vm._v(\" \"),_c('div',{staticClass:\"color-item\"},[_c('h4',[_vm._v(_vm._s(_vm.$t('settings.style.advanced_colors.buttons')))]),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"btnColor\",\"fallback\":_vm.fgColorLocal,\"label\":_vm.$t('settings.background')},model:{value:(_vm.btnColorLocal),callback:function ($$v) {_vm.btnColorLocal=$$v},expression:\"btnColorLocal\"}}),_vm._v(\" \"),_c('OpacityInput',{attrs:{\"name\":\"btnOpacity\",\"fallback\":_vm.previewTheme.opacity.btn || 1},model:{value:(_vm.btnOpacityLocal),callback:function ($$v) {_vm.btnOpacityLocal=$$v},expression:\"btnOpacityLocal\"}}),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"btnTextColor\",\"fallback\":_vm.previewTheme.colors.btnText,\"label\":_vm.$t('settings.text')},model:{value:(_vm.btnTextColorLocal),callback:function ($$v) {_vm.btnTextColorLocal=$$v},expression:\"btnTextColorLocal\"}}),_vm._v(\" \"),_c('ContrastRatio',{attrs:{\"contrast\":_vm.previewContrast.btnText}})],1),_vm._v(\" \"),_c('div',{staticClass:\"color-item\"},[_c('h4',[_vm._v(_vm._s(_vm.$t('settings.style.advanced_colors.borders')))]),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"borderColor\",\"fallback\":_vm.previewTheme.colors.border,\"label\":_vm.$t('settings.style.common.color')},model:{value:(_vm.borderColorLocal),callback:function ($$v) {_vm.borderColorLocal=$$v},expression:\"borderColorLocal\"}}),_vm._v(\" \"),_c('OpacityInput',{attrs:{\"name\":\"borderOpacity\",\"fallback\":_vm.previewTheme.opacity.border || 1},model:{value:(_vm.borderOpacityLocal),callback:function ($$v) {_vm.borderOpacityLocal=$$v},expression:\"borderOpacityLocal\"}})],1),_vm._v(\" \"),_c('div',{staticClass:\"color-item\"},[_c('h4',[_vm._v(_vm._s(_vm.$t('settings.style.advanced_colors.faint_text')))]),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"faintColor\",\"fallback\":_vm.previewTheme.colors.faint || 1,\"label\":_vm.$t('settings.text')},model:{value:(_vm.faintColorLocal),callback:function ($$v) {_vm.faintColorLocal=$$v},expression:\"faintColorLocal\"}}),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"faintLinkColor\",\"fallback\":_vm.previewTheme.colors.faintLink,\"label\":_vm.$t('settings.links')},model:{value:(_vm.faintLinkColorLocal),callback:function ($$v) {_vm.faintLinkColorLocal=$$v},expression:\"faintLinkColorLocal\"}}),_vm._v(\" \"),_c('ColorInput',{attrs:{\"name\":\"panelFaintColor\",\"fallback\":_vm.previewTheme.colors.panelFaint,\"label\":_vm.$t('settings.style.advanced_colors.panel_header')},model:{value:(_vm.panelFaintColorLocal),callback:function ($$v) {_vm.panelFaintColorLocal=$$v},expression:\"panelFaintColorLocal\"}}),_vm._v(\" \"),_c('OpacityInput',{attrs:{\"name\":\"faintOpacity\",\"fallback\":_vm.previewTheme.opacity.faint || 0.5},model:{value:(_vm.faintOpacityLocal),callback:function ($$v) {_vm.faintOpacityLocal=$$v},expression:\"faintOpacityLocal\"}})],1)]),_vm._v(\" \"),_c('div',{staticClass:\"radius-container\",attrs:{\"label\":_vm.$t('settings.style.radii._tab_label')}},[_c('div',{staticClass:\"tab-header\"},[_c('p',[_vm._v(_vm._s(_vm.$t('settings.radii_help')))]),_vm._v(\" \"),_c('button',{staticClass:\"btn\",on:{\"click\":_vm.clearRoundness}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.style.switcher.clear_all'))+\"\\n \")])]),_vm._v(\" \"),_c('RangeInput',{attrs:{\"name\":\"btnRadius\",\"label\":_vm.$t('settings.btnRadius'),\"fallback\":_vm.previewTheme.radii.btn,\"max\":\"16\",\"hard-min\":\"0\"},model:{value:(_vm.btnRadiusLocal),callback:function ($$v) {_vm.btnRadiusLocal=$$v},expression:\"btnRadiusLocal\"}}),_vm._v(\" \"),_c('RangeInput',{attrs:{\"name\":\"inputRadius\",\"label\":_vm.$t('settings.inputRadius'),\"fallback\":_vm.previewTheme.radii.input,\"max\":\"9\",\"hard-min\":\"0\"},model:{value:(_vm.inputRadiusLocal),callback:function ($$v) {_vm.inputRadiusLocal=$$v},expression:\"inputRadiusLocal\"}}),_vm._v(\" \"),_c('RangeInput',{attrs:{\"name\":\"checkboxRadius\",\"label\":_vm.$t('settings.checkboxRadius'),\"fallback\":_vm.previewTheme.radii.checkbox,\"max\":\"16\",\"hard-min\":\"0\"},model:{value:(_vm.checkboxRadiusLocal),callback:function ($$v) {_vm.checkboxRadiusLocal=$$v},expression:\"checkboxRadiusLocal\"}}),_vm._v(\" \"),_c('RangeInput',{attrs:{\"name\":\"panelRadius\",\"label\":_vm.$t('settings.panelRadius'),\"fallback\":_vm.previewTheme.radii.panel,\"max\":\"50\",\"hard-min\":\"0\"},model:{value:(_vm.panelRadiusLocal),callback:function ($$v) {_vm.panelRadiusLocal=$$v},expression:\"panelRadiusLocal\"}}),_vm._v(\" \"),_c('RangeInput',{attrs:{\"name\":\"avatarRadius\",\"label\":_vm.$t('settings.avatarRadius'),\"fallback\":_vm.previewTheme.radii.avatar,\"max\":\"28\",\"hard-min\":\"0\"},model:{value:(_vm.avatarRadiusLocal),callback:function ($$v) {_vm.avatarRadiusLocal=$$v},expression:\"avatarRadiusLocal\"}}),_vm._v(\" \"),_c('RangeInput',{attrs:{\"name\":\"avatarAltRadius\",\"label\":_vm.$t('settings.avatarAltRadius'),\"fallback\":_vm.previewTheme.radii.avatarAlt,\"max\":\"28\",\"hard-min\":\"0\"},model:{value:(_vm.avatarAltRadiusLocal),callback:function ($$v) {_vm.avatarAltRadiusLocal=$$v},expression:\"avatarAltRadiusLocal\"}}),_vm._v(\" \"),_c('RangeInput',{attrs:{\"name\":\"attachmentRadius\",\"label\":_vm.$t('settings.attachmentRadius'),\"fallback\":_vm.previewTheme.radii.attachment,\"max\":\"50\",\"hard-min\":\"0\"},model:{value:(_vm.attachmentRadiusLocal),callback:function ($$v) {_vm.attachmentRadiusLocal=$$v},expression:\"attachmentRadiusLocal\"}}),_vm._v(\" \"),_c('RangeInput',{attrs:{\"name\":\"tooltipRadius\",\"label\":_vm.$t('settings.tooltipRadius'),\"fallback\":_vm.previewTheme.radii.tooltip,\"max\":\"50\",\"hard-min\":\"0\"},model:{value:(_vm.tooltipRadiusLocal),callback:function ($$v) {_vm.tooltipRadiusLocal=$$v},expression:\"tooltipRadiusLocal\"}})],1),_vm._v(\" \"),_c('div',{staticClass:\"shadow-container\",attrs:{\"label\":_vm.$t('settings.style.shadows._tab_label')}},[_c('div',{staticClass:\"tab-header shadow-selector\"},[_c('div',{staticClass:\"select-container\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.style.shadows.component'))+\"\\n \"),_c('label',{staticClass:\"select\",attrs:{\"for\":\"shadow-switcher\"}},[_c('select',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.shadowSelected),expression:\"shadowSelected\"}],staticClass:\"shadow-switcher\",attrs:{\"id\":\"shadow-switcher\"},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.shadowSelected=$event.target.multiple ? $$selectedVal : $$selectedVal[0]}}},_vm._l((_vm.shadowsAvailable),function(shadow){return _c('option',{key:shadow,domProps:{\"value\":shadow}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.style.shadows.components.' + shadow))+\"\\n \")])}),0),_vm._v(\" \"),_c('i',{staticClass:\"icon-down-open\"})])]),_vm._v(\" \"),_c('div',{staticClass:\"override\"},[_c('label',{staticClass:\"label\",attrs:{\"for\":\"override\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.style.shadows.override'))+\"\\n \")]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.currentShadowOverriden),expression:\"currentShadowOverriden\"}],staticClass:\"input-override\",attrs:{\"id\":\"override\",\"name\":\"override\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.currentShadowOverriden)?_vm._i(_vm.currentShadowOverriden,null)>-1:(_vm.currentShadowOverriden)},on:{\"change\":function($event){var $$a=_vm.currentShadowOverriden,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.currentShadowOverriden=$$a.concat([$$v]))}else{$$i>-1&&(_vm.currentShadowOverriden=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.currentShadowOverriden=$$c}}}}),_vm._v(\" \"),_c('label',{staticClass:\"checkbox-label\",attrs:{\"for\":\"override\"}})]),_vm._v(\" \"),_c('button',{staticClass:\"btn\",on:{\"click\":_vm.clearShadows}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.style.switcher.clear_all'))+\"\\n \")])]),_vm._v(\" \"),_c('shadow-control',{attrs:{\"ready\":!!_vm.currentShadowFallback,\"fallback\":_vm.currentShadowFallback},model:{value:(_vm.currentShadow),callback:function ($$v) {_vm.currentShadow=$$v},expression:\"currentShadow\"}}),_vm._v(\" \"),(_vm.shadowSelected === 'avatar' || _vm.shadowSelected === 'avatarStatus')?_c('div',[_c('i18n',{attrs:{\"path\":\"settings.style.shadows.filter_hint.always_drop_shadow\",\"tag\":\"p\"}},[_c('code',[_vm._v(\"filter: drop-shadow()\")])]),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.style.shadows.filter_hint.avatar_inset')))]),_vm._v(\" \"),_c('i18n',{attrs:{\"path\":\"settings.style.shadows.filter_hint.drop_shadow_syntax\",\"tag\":\"p\"}},[_c('code',[_vm._v(\"drop-shadow\")]),_vm._v(\" \"),_c('code',[_vm._v(\"spread-radius\")]),_vm._v(\" \"),_c('code',[_vm._v(\"inset\")])]),_vm._v(\" \"),_c('i18n',{attrs:{\"path\":\"settings.style.shadows.filter_hint.inset_classic\",\"tag\":\"p\"}},[_c('code',[_vm._v(\"box-shadow\")])]),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.style.shadows.filter_hint.spread_zero')))])],1):_vm._e()],1),_vm._v(\" \"),_c('div',{staticClass:\"fonts-container\",attrs:{\"label\":_vm.$t('settings.style.fonts._tab_label')}},[_c('div',{staticClass:\"tab-header\"},[_c('p',[_vm._v(_vm._s(_vm.$t('settings.style.fonts.help')))]),_vm._v(\" \"),_c('button',{staticClass:\"btn\",on:{\"click\":_vm.clearFonts}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.style.switcher.clear_all'))+\"\\n \")])]),_vm._v(\" \"),_c('FontControl',{attrs:{\"name\":\"ui\",\"label\":_vm.$t('settings.style.fonts.components.interface'),\"fallback\":_vm.previewTheme.fonts.interface,\"no-inherit\":\"1\"},model:{value:(_vm.fontsLocal.interface),callback:function ($$v) {_vm.$set(_vm.fontsLocal, \"interface\", $$v)},expression:\"fontsLocal.interface\"}}),_vm._v(\" \"),_c('FontControl',{attrs:{\"name\":\"input\",\"label\":_vm.$t('settings.style.fonts.components.input'),\"fallback\":_vm.previewTheme.fonts.input},model:{value:(_vm.fontsLocal.input),callback:function ($$v) {_vm.$set(_vm.fontsLocal, \"input\", $$v)},expression:\"fontsLocal.input\"}}),_vm._v(\" \"),_c('FontControl',{attrs:{\"name\":\"post\",\"label\":_vm.$t('settings.style.fonts.components.post'),\"fallback\":_vm.previewTheme.fonts.post},model:{value:(_vm.fontsLocal.post),callback:function ($$v) {_vm.$set(_vm.fontsLocal, \"post\", $$v)},expression:\"fontsLocal.post\"}}),_vm._v(\" \"),_c('FontControl',{attrs:{\"name\":\"postCode\",\"label\":_vm.$t('settings.style.fonts.components.postCode'),\"fallback\":_vm.previewTheme.fonts.postCode},model:{value:(_vm.fontsLocal.postCode),callback:function ($$v) {_vm.$set(_vm.fontsLocal, \"postCode\", $$v)},expression:\"fontsLocal.postCode\"}})],1)])],1),_vm._v(\" \"),_c('div',{staticClass:\"apply-container\"},[_c('button',{staticClass:\"btn submit\",attrs:{\"disabled\":!_vm.themeValid},on:{\"click\":_vm.setCustomTheme}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.apply'))+\"\\n \")]),_vm._v(\" \"),_c('button',{staticClass:\"btn\",on:{\"click\":_vm.clearAll}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.style.switcher.reset'))+\"\\n \")])])],1)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('label',{attrs:{\"for\":\"interface-language-switcher\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.interfaceLanguage'))+\"\\n \")]),_vm._v(\" \"),_c('label',{staticClass:\"select\",attrs:{\"for\":\"interface-language-switcher\"}},[_c('select',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.language),expression:\"language\"}],attrs:{\"id\":\"interface-language-switcher\"},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.language=$event.target.multiple ? $$selectedVal : $$selectedVal[0]}}},_vm._l((_vm.languageCodes),function(langCode,i){return _c('option',{key:langCode,domProps:{\"value\":langCode}},[_vm._v(\"\\n \"+_vm._s(_vm.languageNames[i])+\"\\n \")])}),0),_vm._v(\" \"),_c('i',{staticClass:\"icon-down-open\"})])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"settings panel panel-default\"},[_c('div',{staticClass:\"panel-heading\"},[_c('div',{staticClass:\"title\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.settings'))+\"\\n \")]),_vm._v(\" \"),_c('transition',{attrs:{\"name\":\"fade\"}},[(_vm.currentSaveStateNotice)?[(_vm.currentSaveStateNotice.error)?_c('div',{staticClass:\"alert error\",on:{\"click\":function($event){$event.preventDefault();}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.saving_err'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(!_vm.currentSaveStateNotice.error)?_c('div',{staticClass:\"alert transparent\",on:{\"click\":function($event){$event.preventDefault();}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.saving_ok'))+\"\\n \")]):_vm._e()]:_vm._e()],2)],1),_vm._v(\" \"),_c('div',{staticClass:\"panel-body\"},[_c('keep-alive',[_c('tab-switcher',[_c('div',{attrs:{\"label\":_vm.$t('settings.general')}},[_c('div',{staticClass:\"setting-item\"},[_c('h2',[_vm._v(_vm._s(_vm.$t('settings.interface')))]),_vm._v(\" \"),_c('ul',{staticClass:\"setting-list\"},[_c('li',[_c('interface-language-switcher')],1),_vm._v(\" \"),(_vm.instanceSpecificPanelPresent)?_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.hideISPLocal),expression:\"hideISPLocal\"}],attrs:{\"id\":\"hideISP\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.hideISPLocal)?_vm._i(_vm.hideISPLocal,null)>-1:(_vm.hideISPLocal)},on:{\"change\":function($event){var $$a=_vm.hideISPLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.hideISPLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.hideISPLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.hideISPLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"hideISP\"}},[_vm._v(_vm._s(_vm.$t('settings.hide_isp')))])]):_vm._e()])]),_vm._v(\" \"),_c('div',{staticClass:\"setting-item\"},[_c('h2',[_vm._v(_vm._s(_vm.$t('nav.timeline')))]),_vm._v(\" \"),_c('ul',{staticClass:\"setting-list\"},[_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.hideMutedPostsLocal),expression:\"hideMutedPostsLocal\"}],attrs:{\"id\":\"hideMutedPosts\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.hideMutedPostsLocal)?_vm._i(_vm.hideMutedPostsLocal,null)>-1:(_vm.hideMutedPostsLocal)},on:{\"change\":function($event){var $$a=_vm.hideMutedPostsLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.hideMutedPostsLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.hideMutedPostsLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.hideMutedPostsLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"hideMutedPosts\"}},[_vm._v(_vm._s(_vm.$t('settings.hide_muted_posts'))+\" \"+_vm._s(_vm.$t('settings.instance_default', { value: _vm.hideMutedPostsDefault })))])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.collapseMessageWithSubjectLocal),expression:\"collapseMessageWithSubjectLocal\"}],attrs:{\"id\":\"collapseMessageWithSubject\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.collapseMessageWithSubjectLocal)?_vm._i(_vm.collapseMessageWithSubjectLocal,null)>-1:(_vm.collapseMessageWithSubjectLocal)},on:{\"change\":function($event){var $$a=_vm.collapseMessageWithSubjectLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.collapseMessageWithSubjectLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.collapseMessageWithSubjectLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.collapseMessageWithSubjectLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"collapseMessageWithSubject\"}},[_vm._v(_vm._s(_vm.$t('settings.collapse_subject'))+\" \"+_vm._s(_vm.$t('settings.instance_default', { value: _vm.collapseMessageWithSubjectDefault })))])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.streamingLocal),expression:\"streamingLocal\"}],attrs:{\"id\":\"streaming\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.streamingLocal)?_vm._i(_vm.streamingLocal,null)>-1:(_vm.streamingLocal)},on:{\"change\":function($event){var $$a=_vm.streamingLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.streamingLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.streamingLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.streamingLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"streaming\"}},[_vm._v(_vm._s(_vm.$t('settings.streaming')))]),_vm._v(\" \"),_c('ul',{staticClass:\"setting-list suboptions\",class:[{disabled: !_vm.streamingLocal}]},[_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.pauseOnUnfocusedLocal),expression:\"pauseOnUnfocusedLocal\"}],attrs:{\"id\":\"pauseOnUnfocused\",\"disabled\":!_vm.streamingLocal,\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.pauseOnUnfocusedLocal)?_vm._i(_vm.pauseOnUnfocusedLocal,null)>-1:(_vm.pauseOnUnfocusedLocal)},on:{\"change\":function($event){var $$a=_vm.pauseOnUnfocusedLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.pauseOnUnfocusedLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.pauseOnUnfocusedLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.pauseOnUnfocusedLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"pauseOnUnfocused\"}},[_vm._v(_vm._s(_vm.$t('settings.pause_on_unfocused')))])])])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.autoLoadLocal),expression:\"autoLoadLocal\"}],attrs:{\"id\":\"autoload\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.autoLoadLocal)?_vm._i(_vm.autoLoadLocal,null)>-1:(_vm.autoLoadLocal)},on:{\"change\":function($event){var $$a=_vm.autoLoadLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.autoLoadLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.autoLoadLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.autoLoadLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"autoload\"}},[_vm._v(_vm._s(_vm.$t('settings.autoload')))])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.hoverPreviewLocal),expression:\"hoverPreviewLocal\"}],attrs:{\"id\":\"hoverPreview\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.hoverPreviewLocal)?_vm._i(_vm.hoverPreviewLocal,null)>-1:(_vm.hoverPreviewLocal)},on:{\"change\":function($event){var $$a=_vm.hoverPreviewLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.hoverPreviewLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.hoverPreviewLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.hoverPreviewLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"hoverPreview\"}},[_vm._v(_vm._s(_vm.$t('settings.reply_link_preview')))])])])]),_vm._v(\" \"),_c('div',{staticClass:\"setting-item\"},[_c('h2',[_vm._v(_vm._s(_vm.$t('settings.composing')))]),_vm._v(\" \"),_c('ul',{staticClass:\"setting-list\"},[_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.scopeCopyLocal),expression:\"scopeCopyLocal\"}],attrs:{\"id\":\"scopeCopy\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.scopeCopyLocal)?_vm._i(_vm.scopeCopyLocal,null)>-1:(_vm.scopeCopyLocal)},on:{\"change\":function($event){var $$a=_vm.scopeCopyLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.scopeCopyLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.scopeCopyLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.scopeCopyLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"scopeCopy\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.scope_copy'))+\" \"+_vm._s(_vm.$t('settings.instance_default', { value: _vm.scopeCopyDefault }))+\"\\n \")])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.alwaysShowSubjectInputLocal),expression:\"alwaysShowSubjectInputLocal\"}],attrs:{\"id\":\"subjectHide\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.alwaysShowSubjectInputLocal)?_vm._i(_vm.alwaysShowSubjectInputLocal,null)>-1:(_vm.alwaysShowSubjectInputLocal)},on:{\"change\":function($event){var $$a=_vm.alwaysShowSubjectInputLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.alwaysShowSubjectInputLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.alwaysShowSubjectInputLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.alwaysShowSubjectInputLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"subjectHide\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.subject_input_always_show'))+\" \"+_vm._s(_vm.$t('settings.instance_default', { value: _vm.alwaysShowSubjectInputDefault }))+\"\\n \")])]),_vm._v(\" \"),_c('li',[_c('div',[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.subject_line_behavior'))+\"\\n \"),_c('label',{staticClass:\"select\",attrs:{\"for\":\"subjectLineBehavior\"}},[_c('select',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.subjectLineBehaviorLocal),expression:\"subjectLineBehaviorLocal\"}],attrs:{\"id\":\"subjectLineBehavior\"},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.subjectLineBehaviorLocal=$event.target.multiple ? $$selectedVal : $$selectedVal[0]}}},[_c('option',{attrs:{\"value\":\"email\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.subject_line_email'))+\"\\n \"+_vm._s(_vm.subjectLineBehaviorDefault == 'email' ? _vm.$t('settings.instance_default_simple') : '')+\"\\n \")]),_vm._v(\" \"),_c('option',{attrs:{\"value\":\"masto\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.subject_line_mastodon'))+\"\\n \"+_vm._s(_vm.subjectLineBehaviorDefault == 'mastodon' ? _vm.$t('settings.instance_default_simple') : '')+\"\\n \")]),_vm._v(\" \"),_c('option',{attrs:{\"value\":\"noop\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.subject_line_noop'))+\"\\n \"+_vm._s(_vm.subjectLineBehaviorDefault == 'noop' ? _vm.$t('settings.instance_default_simple') : '')+\"\\n \")])]),_vm._v(\" \"),_c('i',{staticClass:\"icon-down-open\"})])])]),_vm._v(\" \"),(_vm.postFormats.length > 0)?_c('li',[_c('div',[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.post_status_content_type'))+\"\\n \"),_c('label',{staticClass:\"select\",attrs:{\"for\":\"postContentType\"}},[_c('select',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.postContentTypeLocal),expression:\"postContentTypeLocal\"}],attrs:{\"id\":\"postContentType\"},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.postContentTypeLocal=$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 \"+_vm._s(_vm.postContentTypeDefault === postFormat ? _vm.$t('settings.instance_default_simple') : '')+\"\\n \")])}),0),_vm._v(\" \"),_c('i',{staticClass:\"icon-down-open\"})])])]):_vm._e(),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.minimalScopesModeLocal),expression:\"minimalScopesModeLocal\"}],attrs:{\"id\":\"minimalScopesMode\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.minimalScopesModeLocal)?_vm._i(_vm.minimalScopesModeLocal,null)>-1:(_vm.minimalScopesModeLocal)},on:{\"change\":function($event){var $$a=_vm.minimalScopesModeLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.minimalScopesModeLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.minimalScopesModeLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.minimalScopesModeLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"minimalScopesMode\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.minimal_scopes_mode'))+\" \"+_vm._s(_vm.$t('settings.instance_default', { value: _vm.minimalScopesModeDefault }))+\"\\n \")])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.autohideFloatingPostButtonLocal),expression:\"autohideFloatingPostButtonLocal\"}],attrs:{\"id\":\"autohideFloatingPostButton\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.autohideFloatingPostButtonLocal)?_vm._i(_vm.autohideFloatingPostButtonLocal,null)>-1:(_vm.autohideFloatingPostButtonLocal)},on:{\"change\":function($event){var $$a=_vm.autohideFloatingPostButtonLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.autohideFloatingPostButtonLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.autohideFloatingPostButtonLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.autohideFloatingPostButtonLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"autohideFloatingPostButton\"}},[_vm._v(_vm._s(_vm.$t('settings.autohide_floating_post_button')))])])])]),_vm._v(\" \"),_c('div',{staticClass:\"setting-item\"},[_c('h2',[_vm._v(_vm._s(_vm.$t('settings.attachments')))]),_vm._v(\" \"),_c('ul',{staticClass:\"setting-list\"},[_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.hideAttachmentsLocal),expression:\"hideAttachmentsLocal\"}],attrs:{\"id\":\"hideAttachments\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.hideAttachmentsLocal)?_vm._i(_vm.hideAttachmentsLocal,null)>-1:(_vm.hideAttachmentsLocal)},on:{\"change\":function($event){var $$a=_vm.hideAttachmentsLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.hideAttachmentsLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.hideAttachmentsLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.hideAttachmentsLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"hideAttachments\"}},[_vm._v(_vm._s(_vm.$t('settings.hide_attachments_in_tl')))])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.hideAttachmentsInConvLocal),expression:\"hideAttachmentsInConvLocal\"}],attrs:{\"id\":\"hideAttachmentsInConv\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.hideAttachmentsInConvLocal)?_vm._i(_vm.hideAttachmentsInConvLocal,null)>-1:(_vm.hideAttachmentsInConvLocal)},on:{\"change\":function($event){var $$a=_vm.hideAttachmentsInConvLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.hideAttachmentsInConvLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.hideAttachmentsInConvLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.hideAttachmentsInConvLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"hideAttachmentsInConv\"}},[_vm._v(_vm._s(_vm.$t('settings.hide_attachments_in_convo')))])]),_vm._v(\" \"),_c('li',[_c('label',{attrs:{\"for\":\"maxThumbnails\"}},[_vm._v(_vm._s(_vm.$t('settings.max_thumbnails')))]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model.number\",value:(_vm.maxThumbnails),expression:\"maxThumbnails\",modifiers:{\"number\":true}}],staticClass:\"number-input\",attrs:{\"id\":\"maxThumbnails\",\"type\":\"number\",\"min\":\"0\",\"step\":\"1\"},domProps:{\"value\":(_vm.maxThumbnails)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.maxThumbnails=_vm._n($event.target.value)},\"blur\":function($event){_vm.$forceUpdate()}}})]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.hideNsfwLocal),expression:\"hideNsfwLocal\"}],attrs:{\"id\":\"hideNsfw\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.hideNsfwLocal)?_vm._i(_vm.hideNsfwLocal,null)>-1:(_vm.hideNsfwLocal)},on:{\"change\":function($event){var $$a=_vm.hideNsfwLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.hideNsfwLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.hideNsfwLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.hideNsfwLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"hideNsfw\"}},[_vm._v(_vm._s(_vm.$t('settings.nsfw_clickthrough')))])]),_vm._v(\" \"),_c('ul',{staticClass:\"setting-list suboptions\"},[_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.preloadImage),expression:\"preloadImage\"}],attrs:{\"id\":\"preloadImage\",\"disabled\":!_vm.hideNsfwLocal,\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.preloadImage)?_vm._i(_vm.preloadImage,null)>-1:(_vm.preloadImage)},on:{\"change\":function($event){var $$a=_vm.preloadImage,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.preloadImage=$$a.concat([$$v]))}else{$$i>-1&&(_vm.preloadImage=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.preloadImage=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"preloadImage\"}},[_vm._v(_vm._s(_vm.$t('settings.preload_images')))])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.useOneClickNsfw),expression:\"useOneClickNsfw\"}],attrs:{\"id\":\"useOneClickNsfw\",\"disabled\":!_vm.hideNsfwLocal,\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.useOneClickNsfw)?_vm._i(_vm.useOneClickNsfw,null)>-1:(_vm.useOneClickNsfw)},on:{\"change\":function($event){var $$a=_vm.useOneClickNsfw,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.useOneClickNsfw=$$a.concat([$$v]))}else{$$i>-1&&(_vm.useOneClickNsfw=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.useOneClickNsfw=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"useOneClickNsfw\"}},[_vm._v(_vm._s(_vm.$t('settings.use_one_click_nsfw')))])])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.stopGifs),expression:\"stopGifs\"}],attrs:{\"id\":\"stopGifs\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.stopGifs)?_vm._i(_vm.stopGifs,null)>-1:(_vm.stopGifs)},on:{\"change\":function($event){var $$a=_vm.stopGifs,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.stopGifs=$$a.concat([$$v]))}else{$$i>-1&&(_vm.stopGifs=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.stopGifs=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"stopGifs\"}},[_vm._v(_vm._s(_vm.$t('settings.stop_gifs')))])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.loopVideoLocal),expression:\"loopVideoLocal\"}],attrs:{\"id\":\"loopVideo\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.loopVideoLocal)?_vm._i(_vm.loopVideoLocal,null)>-1:(_vm.loopVideoLocal)},on:{\"change\":function($event){var $$a=_vm.loopVideoLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.loopVideoLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.loopVideoLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.loopVideoLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"loopVideo\"}},[_vm._v(_vm._s(_vm.$t('settings.loop_video')))]),_vm._v(\" \"),_c('ul',{staticClass:\"setting-list suboptions\",class:[{disabled: !_vm.streamingLocal}]},[_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.loopVideoSilentOnlyLocal),expression:\"loopVideoSilentOnlyLocal\"}],attrs:{\"id\":\"loopVideoSilentOnly\",\"disabled\":!_vm.loopVideoLocal || !_vm.loopSilentAvailable,\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.loopVideoSilentOnlyLocal)?_vm._i(_vm.loopVideoSilentOnlyLocal,null)>-1:(_vm.loopVideoSilentOnlyLocal)},on:{\"change\":function($event){var $$a=_vm.loopVideoSilentOnlyLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.loopVideoSilentOnlyLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.loopVideoSilentOnlyLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.loopVideoSilentOnlyLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"loopVideoSilentOnly\"}},[_vm._v(_vm._s(_vm.$t('settings.loop_video_silent_only')))]),_vm._v(\" \"),(!_vm.loopSilentAvailable)?_c('div',{staticClass:\"unavailable\"},[_c('i',{staticClass:\"icon-globe\"}),_vm._v(\"! \"+_vm._s(_vm.$t('settings.limited_availability'))+\"\\n \")]):_vm._e()])])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.playVideosInModal),expression:\"playVideosInModal\"}],attrs:{\"id\":\"playVideosInModal\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.playVideosInModal)?_vm._i(_vm.playVideosInModal,null)>-1:(_vm.playVideosInModal)},on:{\"change\":function($event){var $$a=_vm.playVideosInModal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.playVideosInModal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.playVideosInModal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.playVideosInModal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"playVideosInModal\"}},[_vm._v(_vm._s(_vm.$t('settings.play_videos_in_modal')))])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.useContainFit),expression:\"useContainFit\"}],attrs:{\"id\":\"useContainFit\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.useContainFit)?_vm._i(_vm.useContainFit,null)>-1:(_vm.useContainFit)},on:{\"change\":function($event){var $$a=_vm.useContainFit,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.useContainFit=$$a.concat([$$v]))}else{$$i>-1&&(_vm.useContainFit=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.useContainFit=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"useContainFit\"}},[_vm._v(_vm._s(_vm.$t('settings.use_contain_fit')))])])])]),_vm._v(\" \"),_c('div',{staticClass:\"setting-item\"},[_c('h2',[_vm._v(_vm._s(_vm.$t('settings.notifications')))]),_vm._v(\" \"),_c('ul',{staticClass:\"setting-list\"},[_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.webPushNotificationsLocal),expression:\"webPushNotificationsLocal\"}],attrs:{\"id\":\"webPushNotifications\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.webPushNotificationsLocal)?_vm._i(_vm.webPushNotificationsLocal,null)>-1:(_vm.webPushNotificationsLocal)},on:{\"change\":function($event){var $$a=_vm.webPushNotificationsLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.webPushNotificationsLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.webPushNotificationsLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.webPushNotificationsLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"webPushNotifications\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.enable_web_push_notifications'))+\"\\n \")])])])])]),_vm._v(\" \"),_c('div',{attrs:{\"label\":_vm.$t('settings.theme')}},[_c('div',{staticClass:\"setting-item\"},[_c('style-switcher')],1)]),_vm._v(\" \"),_c('div',{attrs:{\"label\":_vm.$t('settings.filtering')}},[_c('div',{staticClass:\"setting-item\"},[_c('div',{staticClass:\"select-multiple\"},[_c('span',{staticClass:\"label\"},[_vm._v(_vm._s(_vm.$t('settings.notification_visibility')))]),_vm._v(\" \"),_c('ul',{staticClass:\"option-list\"},[_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.notificationVisibilityLocal.likes),expression:\"notificationVisibilityLocal.likes\"}],attrs:{\"id\":\"notification-visibility-likes\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.notificationVisibilityLocal.likes)?_vm._i(_vm.notificationVisibilityLocal.likes,null)>-1:(_vm.notificationVisibilityLocal.likes)},on:{\"change\":function($event){var $$a=_vm.notificationVisibilityLocal.likes,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.$set(_vm.notificationVisibilityLocal, \"likes\", $$a.concat([$$v])))}else{$$i>-1&&(_vm.$set(_vm.notificationVisibilityLocal, \"likes\", $$a.slice(0,$$i).concat($$a.slice($$i+1))))}}else{_vm.$set(_vm.notificationVisibilityLocal, \"likes\", $$c)}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"notification-visibility-likes\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.notification_visibility_likes'))+\"\\n \")])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.notificationVisibilityLocal.repeats),expression:\"notificationVisibilityLocal.repeats\"}],attrs:{\"id\":\"notification-visibility-repeats\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.notificationVisibilityLocal.repeats)?_vm._i(_vm.notificationVisibilityLocal.repeats,null)>-1:(_vm.notificationVisibilityLocal.repeats)},on:{\"change\":function($event){var $$a=_vm.notificationVisibilityLocal.repeats,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.$set(_vm.notificationVisibilityLocal, \"repeats\", $$a.concat([$$v])))}else{$$i>-1&&(_vm.$set(_vm.notificationVisibilityLocal, \"repeats\", $$a.slice(0,$$i).concat($$a.slice($$i+1))))}}else{_vm.$set(_vm.notificationVisibilityLocal, \"repeats\", $$c)}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"notification-visibility-repeats\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.notification_visibility_repeats'))+\"\\n \")])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.notificationVisibilityLocal.follows),expression:\"notificationVisibilityLocal.follows\"}],attrs:{\"id\":\"notification-visibility-follows\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.notificationVisibilityLocal.follows)?_vm._i(_vm.notificationVisibilityLocal.follows,null)>-1:(_vm.notificationVisibilityLocal.follows)},on:{\"change\":function($event){var $$a=_vm.notificationVisibilityLocal.follows,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.$set(_vm.notificationVisibilityLocal, \"follows\", $$a.concat([$$v])))}else{$$i>-1&&(_vm.$set(_vm.notificationVisibilityLocal, \"follows\", $$a.slice(0,$$i).concat($$a.slice($$i+1))))}}else{_vm.$set(_vm.notificationVisibilityLocal, \"follows\", $$c)}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"notification-visibility-follows\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.notification_visibility_follows'))+\"\\n \")])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.notificationVisibilityLocal.mentions),expression:\"notificationVisibilityLocal.mentions\"}],attrs:{\"id\":\"notification-visibility-mentions\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.notificationVisibilityLocal.mentions)?_vm._i(_vm.notificationVisibilityLocal.mentions,null)>-1:(_vm.notificationVisibilityLocal.mentions)},on:{\"change\":function($event){var $$a=_vm.notificationVisibilityLocal.mentions,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.$set(_vm.notificationVisibilityLocal, \"mentions\", $$a.concat([$$v])))}else{$$i>-1&&(_vm.$set(_vm.notificationVisibilityLocal, \"mentions\", $$a.slice(0,$$i).concat($$a.slice($$i+1))))}}else{_vm.$set(_vm.notificationVisibilityLocal, \"mentions\", $$c)}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"notification-visibility-mentions\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.notification_visibility_mentions'))+\"\\n \")])])])]),_vm._v(\" \"),_c('div',[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.replies_in_timeline'))+\"\\n \"),_c('label',{staticClass:\"select\",attrs:{\"for\":\"replyVisibility\"}},[_c('select',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.replyVisibilityLocal),expression:\"replyVisibilityLocal\"}],attrs:{\"id\":\"replyVisibility\"},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.replyVisibilityLocal=$event.target.multiple ? $$selectedVal : $$selectedVal[0]}}},[_c('option',{attrs:{\"value\":\"all\",\"selected\":\"\"}},[_vm._v(_vm._s(_vm.$t('settings.reply_visibility_all')))]),_vm._v(\" \"),_c('option',{attrs:{\"value\":\"following\"}},[_vm._v(_vm._s(_vm.$t('settings.reply_visibility_following')))]),_vm._v(\" \"),_c('option',{attrs:{\"value\":\"self\"}},[_vm._v(_vm._s(_vm.$t('settings.reply_visibility_self')))])]),_vm._v(\" \"),_c('i',{staticClass:\"icon-down-open\"})])]),_vm._v(\" \"),_c('div',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.hidePostStatsLocal),expression:\"hidePostStatsLocal\"}],attrs:{\"id\":\"hidePostStats\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.hidePostStatsLocal)?_vm._i(_vm.hidePostStatsLocal,null)>-1:(_vm.hidePostStatsLocal)},on:{\"change\":function($event){var $$a=_vm.hidePostStatsLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.hidePostStatsLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.hidePostStatsLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.hidePostStatsLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"hidePostStats\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.hide_post_stats'))+\" \"+_vm._s(_vm.$t('settings.instance_default', { value: _vm.hidePostStatsDefault }))+\"\\n \")])]),_vm._v(\" \"),_c('div',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.hideUserStatsLocal),expression:\"hideUserStatsLocal\"}],attrs:{\"id\":\"hideUserStats\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.hideUserStatsLocal)?_vm._i(_vm.hideUserStatsLocal,null)>-1:(_vm.hideUserStatsLocal)},on:{\"change\":function($event){var $$a=_vm.hideUserStatsLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.hideUserStatsLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.hideUserStatsLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.hideUserStatsLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"hideUserStats\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.hide_user_stats'))+\" \"+_vm._s(_vm.$t('settings.instance_default', { value: _vm.hideUserStatsDefault }))+\"\\n \")])])]),_vm._v(\" \"),_c('div',{staticClass:\"setting-item\"},[_c('div',[_c('p',[_vm._v(_vm._s(_vm.$t('settings.filtering_explanation')))]),_vm._v(\" \"),_c('textarea',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.muteWordsString),expression:\"muteWordsString\"}],attrs:{\"id\":\"muteWords\"},domProps:{\"value\":(_vm.muteWordsString)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.muteWordsString=$event.target.value}}})]),_vm._v(\" \"),_c('div',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.hideFilteredStatusesLocal),expression:\"hideFilteredStatusesLocal\"}],attrs:{\"id\":\"hideFilteredStatuses\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.hideFilteredStatusesLocal)?_vm._i(_vm.hideFilteredStatusesLocal,null)>-1:(_vm.hideFilteredStatusesLocal)},on:{\"change\":function($event){var $$a=_vm.hideFilteredStatusesLocal,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.hideFilteredStatusesLocal=$$a.concat([$$v]))}else{$$i>-1&&(_vm.hideFilteredStatusesLocal=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.hideFilteredStatusesLocal=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"hideFilteredStatuses\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.hide_filtered_statuses'))+\" \"+_vm._s(_vm.$t('settings.instance_default', { value: _vm.hideFilteredStatusesDefault }))+\"\\n \")])])])]),_vm._v(\" \"),_c('div',{attrs:{\"label\":_vm.$t('settings.version.title')}},[_c('div',{staticClass:\"setting-item\"},[_c('ul',{staticClass:\"setting-list\"},[_c('li',[_c('p',[_vm._v(_vm._s(_vm.$t('settings.version.backend_version')))]),_vm._v(\" \"),_c('ul',{staticClass:\"option-list\"},[_c('li',[_c('a',{attrs:{\"href\":_vm.backendVersionLink,\"target\":\"_blank\"}},[_vm._v(_vm._s(_vm.backendVersion))])])])]),_vm._v(\" \"),_c('li',[_c('p',[_vm._v(_vm._s(_vm.$t('settings.version.frontend_version')))]),_vm._v(\" \"),_c('ul',{staticClass:\"option-list\"},[_c('li',[_c('a',{attrs:{\"href\":_vm.frontendVersionLink,\"target\":\"_blank\"}},[_vm._v(_vm._s(_vm.frontendVersion))])])])])])])])])],1)],1)])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"settings panel panel-default\"},[_c('div',{staticClass:\"panel-heading\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('registration.registration'))+\"\\n \")]),_vm._v(\" \"),_c('div',{staticClass:\"panel-body\"},[_c('form',{staticClass:\"registration-form\",on:{\"submit\":function($event){$event.preventDefault();_vm.submit(_vm.user)}}},[_c('div',{staticClass:\"container\"},[_c('div',{staticClass:\"text-fields\"},[_c('div',{staticClass:\"form-group\",class:{ 'form-group--error': _vm.$v.user.username.$error }},[_c('label',{staticClass:\"form--label\",attrs:{\"for\":\"sign-up-username\"}},[_vm._v(_vm._s(_vm.$t('login.username')))]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model.trim\",value:(_vm.$v.user.username.$model),expression:\"$v.user.username.$model\",modifiers:{\"trim\":true}}],staticClass:\"form-control\",attrs:{\"id\":\"sign-up-username\",\"disabled\":_vm.isPending,\"placeholder\":_vm.$t('registration.username_placeholder')},domProps:{\"value\":(_vm.$v.user.username.$model)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.$v.user.username, \"$model\", $event.target.value.trim())},\"blur\":function($event){_vm.$forceUpdate()}}})]),_vm._v(\" \"),(_vm.$v.user.username.$dirty)?_c('div',{staticClass:\"form-error\"},[_c('ul',[(!_vm.$v.user.username.required)?_c('li',[_c('span',[_vm._v(_vm._s(_vm.$t('registration.validations.username_required')))])]):_vm._e()])]):_vm._e(),_vm._v(\" \"),_c('div',{staticClass:\"form-group\",class:{ 'form-group--error': _vm.$v.user.fullname.$error }},[_c('label',{staticClass:\"form--label\",attrs:{\"for\":\"sign-up-fullname\"}},[_vm._v(_vm._s(_vm.$t('registration.fullname')))]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model.trim\",value:(_vm.$v.user.fullname.$model),expression:\"$v.user.fullname.$model\",modifiers:{\"trim\":true}}],staticClass:\"form-control\",attrs:{\"id\":\"sign-up-fullname\",\"disabled\":_vm.isPending,\"placeholder\":_vm.$t('registration.fullname_placeholder')},domProps:{\"value\":(_vm.$v.user.fullname.$model)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.$v.user.fullname, \"$model\", $event.target.value.trim())},\"blur\":function($event){_vm.$forceUpdate()}}})]),_vm._v(\" \"),(_vm.$v.user.fullname.$dirty)?_c('div',{staticClass:\"form-error\"},[_c('ul',[(!_vm.$v.user.fullname.required)?_c('li',[_c('span',[_vm._v(_vm._s(_vm.$t('registration.validations.fullname_required')))])]):_vm._e()])]):_vm._e(),_vm._v(\" \"),_c('div',{staticClass:\"form-group\",class:{ 'form-group--error': _vm.$v.user.email.$error }},[_c('label',{staticClass:\"form--label\",attrs:{\"for\":\"email\"}},[_vm._v(_vm._s(_vm.$t('registration.email')))]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.$v.user.email.$model),expression:\"$v.user.email.$model\"}],staticClass:\"form-control\",attrs:{\"id\":\"email\",\"disabled\":_vm.isPending,\"type\":\"email\"},domProps:{\"value\":(_vm.$v.user.email.$model)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.$v.user.email, \"$model\", $event.target.value)}}})]),_vm._v(\" \"),(_vm.$v.user.email.$dirty)?_c('div',{staticClass:\"form-error\"},[_c('ul',[(!_vm.$v.user.email.required)?_c('li',[_c('span',[_vm._v(_vm._s(_vm.$t('registration.validations.email_required')))])]):_vm._e()])]):_vm._e(),_vm._v(\" \"),_c('div',{staticClass:\"form-group\"},[_c('label',{staticClass:\"form--label\",attrs:{\"for\":\"bio\"}},[_vm._v(_vm._s(_vm.$t('registration.bio'))+\" (\"+_vm._s(_vm.$t('general.optional'))+\")\")]),_vm._v(\" \"),_c('textarea',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.user.bio),expression:\"user.bio\"}],staticClass:\"form-control\",attrs:{\"id\":\"bio\",\"disabled\":_vm.isPending,\"placeholder\":_vm.bioPlaceholder},domProps:{\"value\":(_vm.user.bio)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.user, \"bio\", $event.target.value)}}})]),_vm._v(\" \"),_c('div',{staticClass:\"form-group\",class:{ 'form-group--error': _vm.$v.user.password.$error }},[_c('label',{staticClass:\"form--label\",attrs:{\"for\":\"sign-up-password\"}},[_vm._v(_vm._s(_vm.$t('login.password')))]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.user.password),expression:\"user.password\"}],staticClass:\"form-control\",attrs:{\"id\":\"sign-up-password\",\"disabled\":_vm.isPending,\"type\":\"password\"},domProps:{\"value\":(_vm.user.password)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.user, \"password\", $event.target.value)}}})]),_vm._v(\" \"),(_vm.$v.user.password.$dirty)?_c('div',{staticClass:\"form-error\"},[_c('ul',[(!_vm.$v.user.password.required)?_c('li',[_c('span',[_vm._v(_vm._s(_vm.$t('registration.validations.password_required')))])]):_vm._e()])]):_vm._e(),_vm._v(\" \"),_c('div',{staticClass:\"form-group\",class:{ 'form-group--error': _vm.$v.user.confirm.$error }},[_c('label',{staticClass:\"form--label\",attrs:{\"for\":\"sign-up-password-confirmation\"}},[_vm._v(_vm._s(_vm.$t('registration.password_confirm')))]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.user.confirm),expression:\"user.confirm\"}],staticClass:\"form-control\",attrs:{\"id\":\"sign-up-password-confirmation\",\"disabled\":_vm.isPending,\"type\":\"password\"},domProps:{\"value\":(_vm.user.confirm)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.user, \"confirm\", $event.target.value)}}})]),_vm._v(\" \"),(_vm.$v.user.confirm.$dirty)?_c('div',{staticClass:\"form-error\"},[_c('ul',[(!_vm.$v.user.confirm.required)?_c('li',[_c('span',[_vm._v(_vm._s(_vm.$t('registration.validations.password_confirmation_required')))])]):_vm._e(),_vm._v(\" \"),(!_vm.$v.user.confirm.sameAsPassword)?_c('li',[_c('span',[_vm._v(_vm._s(_vm.$t('registration.validations.password_confirmation_match')))])]):_vm._e()])]):_vm._e(),_vm._v(\" \"),(_vm.captcha.type != 'none')?_c('div',{staticClass:\"form-group\",attrs:{\"id\":\"captcha-group\"}},[_c('label',{staticClass:\"form--label\",attrs:{\"for\":\"captcha-label\"}},[_vm._v(_vm._s(_vm.$t('captcha')))]),_vm._v(\" \"),(_vm.captcha.type == 'kocaptcha')?[_c('img',{attrs:{\"src\":_vm.captcha.url},on:{\"click\":_vm.setCaptcha}}),_vm._v(\" \"),_c('sub',[_vm._v(_vm._s(_vm.$t('registration.new_captcha')))]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.captcha.solution),expression:\"captcha.solution\"}],staticClass:\"form-control\",attrs:{\"id\":\"captcha-answer\",\"disabled\":_vm.isPending,\"type\":\"text\",\"autocomplete\":\"off\"},domProps:{\"value\":(_vm.captcha.solution)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.captcha, \"solution\", $event.target.value)}}})]:_vm._e()],2):_vm._e(),_vm._v(\" \"),(_vm.token)?_c('div',{staticClass:\"form-group\"},[_c('label',{attrs:{\"for\":\"token\"}},[_vm._v(_vm._s(_vm.$t('registration.token')))]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.token),expression:\"token\"}],staticClass:\"form-control\",attrs:{\"id\":\"token\",\"disabled\":\"true\",\"type\":\"text\"},domProps:{\"value\":(_vm.token)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.token=$event.target.value}}})]):_vm._e(),_vm._v(\" \"),_c('div',{staticClass:\"form-group\"},[_c('button',{staticClass:\"btn btn-default\",attrs:{\"disabled\":_vm.isPending,\"type\":\"submit\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.submit'))+\"\\n \")])])]),_vm._v(\" \"),_c('div',{staticClass:\"terms-of-service\",domProps:{\"innerHTML\":_vm._s(_vm.termsOfService)}})]),_vm._v(\" \"),(_vm.serverValidationErrors.length)?_c('div',{staticClass:\"form-group\"},[_c('div',{staticClass:\"alert error\"},_vm._l((_vm.serverValidationErrors),function(error){return _c('span',{key:error},[_vm._v(_vm._s(error))])}),0)]):_vm._e()])])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"settings panel panel-default\"},[_c('div',{staticClass:\"panel-heading\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('password_reset.password_reset'))+\"\\n \")]),_vm._v(\" \"),_c('div',{staticClass:\"panel-body\"},[_c('form',{staticClass:\"password-reset-form\",on:{\"submit\":function($event){$event.preventDefault();return _vm.submit($event)}}},[_c('div',{staticClass:\"container\"},[(!_vm.mailerEnabled)?_c('div',[_c('p',[_vm._v(\"\\n \"+_vm._s(_vm.$t('password_reset.password_reset_disabled'))+\"\\n \")])]):(_vm.success || _vm.throttled)?_c('div',[(_vm.success)?_c('p',[_vm._v(\"\\n \"+_vm._s(_vm.$t('password_reset.check_email'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),_c('div',{staticClass:\"form-group text-center\"},[_c('router-link',{attrs:{\"to\":{name: 'root'}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('password_reset.return_home'))+\"\\n \")])],1)]):_c('div',[_c('p',[_vm._v(\"\\n \"+_vm._s(_vm.$t('password_reset.instruction'))+\"\\n \")]),_vm._v(\" \"),_c('div',{staticClass:\"form-group\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.user.email),expression:\"user.email\"}],ref:\"email\",staticClass:\"form-control\",attrs:{\"disabled\":_vm.isPending,\"placeholder\":_vm.$t('password_reset.placeholder'),\"type\":\"input\"},domProps:{\"value\":(_vm.user.email)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.user, \"email\", $event.target.value)}}})]),_vm._v(\" \"),_c('div',{staticClass:\"form-group\"},[_c('button',{staticClass:\"btn btn-default btn-block\",attrs:{\"disabled\":_vm.isPending,\"type\":\"submit\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.submit'))+\"\\n \")])])]),_vm._v(\" \"),(_vm.error)?_c('p',{staticClass:\"alert error notice-dismissible\"},[_c('span',[_vm._v(_vm._s(_vm.error))]),_vm._v(\" \"),_c('a',{staticClass:\"button-icon dismiss\",on:{\"click\":function($event){$event.preventDefault();_vm.dismissError()}}},[_c('i',{staticClass:\"icon-cancel\"})])]):_vm._e()])])])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"image-cropper\"},[(_vm.dataUrl)?_c('div',[_c('div',{staticClass:\"image-cropper-image-container\"},[_c('img',{ref:\"img\",attrs:{\"src\":_vm.dataUrl,\"alt\":\"\"},on:{\"load\":function($event){$event.stopPropagation();return _vm.createCropper($event)}}})]),_vm._v(\" \"),_c('div',{staticClass:\"image-cropper-buttons-wrapper\"},[_c('button',{staticClass:\"btn\",attrs:{\"type\":\"button\",\"disabled\":_vm.submitting},domProps:{\"textContent\":_vm._s(_vm.saveText)},on:{\"click\":function($event){_vm.submit()}}}),_vm._v(\" \"),_c('button',{staticClass:\"btn\",attrs:{\"type\":\"button\",\"disabled\":_vm.submitting},domProps:{\"textContent\":_vm._s(_vm.cancelText)},on:{\"click\":_vm.destroy}}),_vm._v(\" \"),_c('button',{staticClass:\"btn\",attrs:{\"type\":\"button\",\"disabled\":_vm.submitting},domProps:{\"textContent\":_vm._s(_vm.saveWithoutCroppingText)},on:{\"click\":function($event){_vm.submit(false)}}}),_vm._v(\" \"),(_vm.submitting)?_c('i',{staticClass:\"icon-spin4 animate-spin\"}):_vm._e()]),_vm._v(\" \"),(_vm.submitError)?_c('div',{staticClass:\"alert error\"},[_vm._v(\"\\n \"+_vm._s(_vm.submitErrorMsg)+\"\\n \"),_c('i',{staticClass:\"button-icon icon-cancel\",on:{\"click\":_vm.clearError}})]):_vm._e()]):_vm._e(),_vm._v(\" \"),_c('input',{ref:\"input\",staticClass:\"image-cropper-img-input\",attrs:{\"type\":\"file\",\"accept\":_vm.mimes}})])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('basic-user-card',{attrs:{\"user\":_vm.user}},[_c('div',{staticClass:\"block-card-content-container\"},[(_vm.blocked)?_c('button',{staticClass:\"btn btn-default\",attrs:{\"disabled\":_vm.progress},on:{\"click\":_vm.unblockUser}},[(_vm.progress)?[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.unblock_progress'))+\"\\n \")]:[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.unblock'))+\"\\n \")]],2):_c('button',{staticClass:\"btn btn-default\",attrs:{\"disabled\":_vm.progress},on:{\"click\":_vm.blockUser}},[(_vm.progress)?[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.block_progress'))+\"\\n \")]:[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.block'))+\"\\n \")]],2)])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('basic-user-card',{attrs:{\"user\":_vm.user}},[_c('div',{staticClass:\"mute-card-content-container\"},[(_vm.muted)?_c('button',{staticClass:\"btn btn-default\",attrs:{\"disabled\":_vm.progress},on:{\"click\":_vm.unmuteUser}},[(_vm.progress)?[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.unmute_progress'))+\"\\n \")]:[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.unmute'))+\"\\n \")]],2):_c('button',{staticClass:\"btn btn-default\",attrs:{\"disabled\":_vm.progress},on:{\"click\":_vm.muteUser}},[(_vm.progress)?[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.mute_progress'))+\"\\n \")]:[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.mute'))+\"\\n \")]],2)])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('label',{staticClass:\"checkbox\"},[_c('input',{attrs:{\"type\":\"checkbox\"},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',[_vm._t(\"default\")],2):_vm._e()])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"selectable-list\"},[(_vm.items.length > 0)?_c('div',{staticClass:\"selectable-list-header\"},[_c('div',{staticClass:\"selectable-list-checkbox-wrapper\"},[_c('Checkbox',{attrs:{\"checked\":_vm.allSelected,\"indeterminate\":_vm.someSelected},on:{\"change\":_vm.toggleAll}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('selectable_list.select_all'))+\"\\n \")])],1),_vm._v(\" \"),_c('div',{staticClass:\"selectable-list-header-actions\"},[_vm._t(\"header\",null,{selected:_vm.filteredSelected})],2)]):_vm._e(),_vm._v(\" \"),_c('List',{attrs:{\"items\":_vm.items,\"get-key\":_vm.getKey},scopedSlots:_vm._u([{key:\"item\",fn:function(ref){\nvar item = ref.item;\nreturn [_c('div',{staticClass:\"selectable-list-item-inner\",class:{ 'selectable-list-item-selected-inner': _vm.isSelected(item) }},[_c('div',{staticClass:\"selectable-list-checkbox-wrapper\"},[_c('Checkbox',{attrs:{\"checked\":_vm.isSelected(item)},on:{\"change\":function (checked) { return _vm.toggle(checked, item); }}})],1),_vm._v(\" \"),_vm._t(\"item\",null,{item:item})],2)]}}])},[_c('template',{slot:\"empty\"},[_vm._t(\"empty\")],2)],2)],1)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{directives:[{name:\"click-outside\",rawName:\"v-click-outside\",value:(_vm.onClickOutside),expression:\"onClickOutside\"}],staticClass:\"autosuggest\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.term),expression:\"term\"}],staticClass:\"autosuggest-input\",attrs:{\"placeholder\":_vm.placeholder},domProps:{\"value\":(_vm.term)},on:{\"click\":_vm.onInputClick,\"input\":function($event){if($event.target.composing){ return; }_vm.term=$event.target.value}}}),_vm._v(\" \"),(_vm.resultsVisible && _vm.filtered.length > 0)?_c('div',{staticClass:\"autosuggest-results\"},[_vm._l((_vm.filtered),function(item){return _vm._t(\"default\",null,{item:item})})],2):_vm._e()])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"importer\"},[_c('form',[_c('input',{ref:\"input\",attrs:{\"type\":\"file\"},on:{\"change\":_vm.change}})]),_vm._v(\" \"),(_vm.submitting)?_c('i',{staticClass:\"icon-spin4 animate-spin importer-uploading\"}):_c('button',{staticClass:\"btn btn-default\",on:{\"click\":_vm.submit}},[_vm._v(\"\\n \"+_vm._s(_vm.submitButtonLabel)+\"\\n \")]),_vm._v(\" \"),(_vm.success)?_c('div',[_c('i',{staticClass:\"icon-cross\",on:{\"click\":_vm.dismiss}}),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.successMessage))])]):(_vm.error)?_c('div',[_c('i',{staticClass:\"icon-cross\",on:{\"click\":_vm.dismiss}}),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.errorMessage))])]):_vm._e()])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"exporter\"},[(_vm.processing)?_c('div',[_c('i',{staticClass:\"icon-spin4 animate-spin exporter-processing\"}),_vm._v(\" \"),_c('span',[_vm._v(_vm._s(_vm.processingMessage))])]):_c('button',{staticClass:\"btn btn-default\",on:{\"click\":_vm.process}},[_vm._v(\"\\n \"+_vm._s(_vm.exportButtonLabel)+\"\\n \")])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[(_vm.displayTitle)?_c('h4',[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.mfa.recovery_codes'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(_vm.inProgress)?_c('i',[_vm._v(_vm._s(_vm.$t('settings.mfa.waiting_a_recovery_codes')))]):_vm._e(),_vm._v(\" \"),(_vm.ready)?[_c('p',{staticClass:\"alert warning\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.mfa.recovery_codes_warning'))+\"\\n \")]),_vm._v(\" \"),_c('ul',{staticClass:\"backup-codes\"},_vm._l((_vm.backupCodes.codes),function(code){return _c('li',{key:code},[_vm._v(\"\\n \"+_vm._s(code)+\"\\n \")])}),0)]:_vm._e()],2)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_vm._t(\"default\"),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-default\",attrs:{\"disabled\":_vm.disabled},on:{\"click\":_vm.confirm}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.confirm'))+\"\\n \")]),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-default\",attrs:{\"disabled\":_vm.disabled},on:{\"click\":_vm.cancel}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.cancel'))+\"\\n \")])],2)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('div',{staticClass:\"method-item\"},[_c('strong',[_vm._v(_vm._s(_vm.$t('settings.mfa.otp')))]),_vm._v(\" \"),(!_vm.isActivated)?_c('button',{staticClass:\"btn btn-default\",on:{\"click\":_vm.doActivate}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.enable'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(_vm.isActivated)?_c('button',{staticClass:\"btn btn-default\",attrs:{\"disabled\":_vm.deactivate},on:{\"click\":_vm.doDeactivate}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.disable'))+\"\\n \")]):_vm._e()]),_vm._v(\" \"),(_vm.deactivate)?_c('confirm',{attrs:{\"disabled\":_vm.inProgress},on:{\"confirm\":_vm.confirmDeactivate,\"cancel\":_vm.cancelDeactivate}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.enter_current_password_to_confirm'))+\":\\n \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.currentPassword),expression:\"currentPassword\"}],attrs:{\"type\":\"password\"},domProps:{\"value\":(_vm.currentPassword)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.currentPassword=$event.target.value}}})]):_vm._e(),_vm._v(\" \"),(_vm.error)?_c('div',{staticClass:\"alert error\"},[_vm._v(\"\\n \"+_vm._s(_vm.error)+\"\\n \")]):_vm._e()],1)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return (_vm.readyInit && _vm.settings.available)?_c('div',{staticClass:\"setting-item mfa-settings\"},[_c('div',{staticClass:\"mfa-heading\"},[_c('h2',[_vm._v(_vm._s(_vm.$t('settings.mfa.title')))])]),_vm._v(\" \"),_c('div',[(!_vm.setupInProgress)?_c('div',{staticClass:\"setting-item\"},[_c('h3',[_vm._v(_vm._s(_vm.$t('settings.mfa.authentication_methods')))]),_vm._v(\" \"),_c('totp-item',{attrs:{\"settings\":_vm.settings},on:{\"deactivate\":_vm.fetchSettings,\"activate\":_vm.activateOTP}}),_vm._v(\" \"),_c('br'),_vm._v(\" \"),(_vm.settings.enabled)?_c('div',[(!_vm.confirmNewBackupCodes)?_c('recovery-codes',{attrs:{\"backup-codes\":_vm.backupCodes}}):_vm._e(),_vm._v(\" \"),(!_vm.confirmNewBackupCodes)?_c('button',{staticClass:\"btn btn-default\",on:{\"click\":_vm.getBackupCodes}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.mfa.generate_new_recovery_codes'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(_vm.confirmNewBackupCodes)?_c('div',[_c('confirm',{attrs:{\"disabled\":_vm.backupCodes.inProgress},on:{\"confirm\":_vm.confirmBackupCodes,\"cancel\":_vm.cancelBackupCodes}},[_c('p',{staticClass:\"warning\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.mfa.warning_of_generate_new_codes'))+\"\\n \")])])],1):_vm._e()],1):_vm._e()],1):_vm._e(),_vm._v(\" \"),(_vm.setupInProgress)?_c('div',[_c('h3',[_vm._v(_vm._s(_vm.$t('settings.mfa.setup_otp')))]),_vm._v(\" \"),(!_vm.setupOTPInProgress)?_c('recovery-codes',{attrs:{\"backup-codes\":_vm.backupCodes}}):_vm._e(),_vm._v(\" \"),(_vm.canSetupOTP)?_c('button',{staticClass:\"btn btn-default\",on:{\"click\":_vm.cancelSetup}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.cancel'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(_vm.canSetupOTP)?_c('button',{staticClass:\"btn btn-default\",on:{\"click\":_vm.setupOTP}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.mfa.setup_otp'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(_vm.setupOTPInProgress)?[(_vm.prepareOTP)?_c('i',[_vm._v(_vm._s(_vm.$t('settings.mfa.wait_pre_setup_otp')))]):_vm._e(),_vm._v(\" \"),(_vm.confirmOTP)?_c('div',[_c('div',{staticClass:\"setup-otp\"},[_c('div',{staticClass:\"qr-code\"},[_c('h4',[_vm._v(_vm._s(_vm.$t('settings.mfa.scan.title')))]),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.mfa.scan.desc')))]),_vm._v(\" \"),_c('qrcode',{attrs:{\"value\":_vm.otpSettings.provisioning_uri,\"options\":{ width: 200 }}}),_vm._v(\" \"),_c('p',[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.mfa.scan.secret_code'))+\":\\n \"+_vm._s(_vm.otpSettings.key)+\"\\n \")])],1),_vm._v(\" \"),_c('div',{staticClass:\"verify\"},[_c('h4',[_vm._v(_vm._s(_vm.$t('general.verify')))]),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.mfa.verify.desc')))]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.otpConfirmToken),expression:\"otpConfirmToken\"}],attrs:{\"type\":\"text\"},domProps:{\"value\":(_vm.otpConfirmToken)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.otpConfirmToken=$event.target.value}}}),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.enter_current_password_to_confirm'))+\":\")]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.currentPassword),expression:\"currentPassword\"}],attrs:{\"type\":\"password\"},domProps:{\"value\":(_vm.currentPassword)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.currentPassword=$event.target.value}}}),_vm._v(\" \"),_c('div',{staticClass:\"confirm-otp-actions\"},[_c('button',{staticClass:\"btn btn-default\",on:{\"click\":_vm.doConfirmOTP}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.mfa.confirm_and_enable'))+\"\\n \")]),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-default\",on:{\"click\":_vm.cancelSetup}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.cancel'))+\"\\n \")])]),_vm._v(\" \"),(_vm.error)?_c('div',{staticClass:\"alert error\"},[_vm._v(\"\\n \"+_vm._s(_vm.error)+\"\\n \")]):_vm._e()])])]):_vm._e()]:_vm._e()],2):_vm._e()])]):_vm._e()}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"settings panel panel-default\"},[_c('div',{staticClass:\"panel-heading\"},[_c('div',{staticClass:\"title\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.user_settings'))+\"\\n \")]),_vm._v(\" \"),_c('transition',{attrs:{\"name\":\"fade\"}},[(_vm.currentSaveStateNotice)?[(_vm.currentSaveStateNotice.error)?_c('div',{staticClass:\"alert error\",on:{\"click\":function($event){$event.preventDefault();}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.saving_err'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(!_vm.currentSaveStateNotice.error)?_c('div',{staticClass:\"alert transparent\",on:{\"click\":function($event){$event.preventDefault();}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.saving_ok'))+\"\\n \")]):_vm._e()]:_vm._e()],2)],1),_vm._v(\" \"),_c('div',{staticClass:\"panel-body profile-edit\"},[_c('tab-switcher',[_c('div',{attrs:{\"label\":_vm.$t('settings.profile_tab')}},[_c('div',{staticClass:\"setting-item\"},[_c('h2',[_vm._v(_vm._s(_vm.$t('settings.name_bio')))]),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.name')))]),_vm._v(\" \"),_c('EmojiInput',{attrs:{\"suggest\":_vm.emojiSuggestor},model:{value:(_vm.newName),callback:function ($$v) {_vm.newName=$$v},expression:\"newName\"}},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.newName),expression:\"newName\"}],attrs:{\"id\":\"username\",\"classname\":\"name-changer\"},domProps:{\"value\":(_vm.newName)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.newName=$event.target.value}}})]),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.bio')))]),_vm._v(\" \"),_c('EmojiInput',{attrs:{\"suggest\":_vm.emojiUserSuggestor},model:{value:(_vm.newBio),callback:function ($$v) {_vm.newBio=$$v},expression:\"newBio\"}},[_c('textarea',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.newBio),expression:\"newBio\"}],attrs:{\"classname\":\"bio\"},domProps:{\"value\":(_vm.newBio)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.newBio=$event.target.value}}})]),_vm._v(\" \"),_c('p',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.newLocked),expression:\"newLocked\"}],attrs:{\"id\":\"account-locked\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.newLocked)?_vm._i(_vm.newLocked,null)>-1:(_vm.newLocked)},on:{\"change\":function($event){var $$a=_vm.newLocked,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.newLocked=$$a.concat([$$v]))}else{$$i>-1&&(_vm.newLocked=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.newLocked=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"account-locked\"}},[_vm._v(_vm._s(_vm.$t('settings.lock_account_description')))])]),_vm._v(\" \"),_c('div',[_c('label',{attrs:{\"for\":\"default-vis\"}},[_vm._v(_vm._s(_vm.$t('settings.default_vis')))]),_vm._v(\" \"),_c('div',{staticClass:\"visibility-tray\",attrs:{\"id\":\"default-vis\"}},[_c('scope-selector',{attrs:{\"show-all\":true,\"user-default\":_vm.newDefaultScope,\"initial-scope\":_vm.newDefaultScope,\"on-scope-change\":_vm.changeVis}})],1)]),_vm._v(\" \"),_c('p',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.newNoRichText),expression:\"newNoRichText\"}],attrs:{\"id\":\"account-no-rich-text\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.newNoRichText)?_vm._i(_vm.newNoRichText,null)>-1:(_vm.newNoRichText)},on:{\"change\":function($event){var $$a=_vm.newNoRichText,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.newNoRichText=$$a.concat([$$v]))}else{$$i>-1&&(_vm.newNoRichText=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.newNoRichText=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"account-no-rich-text\"}},[_vm._v(_vm._s(_vm.$t('settings.no_rich_text_description')))])]),_vm._v(\" \"),_c('p',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.hideFollows),expression:\"hideFollows\"}],attrs:{\"id\":\"account-hide-follows\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.hideFollows)?_vm._i(_vm.hideFollows,null)>-1:(_vm.hideFollows)},on:{\"change\":function($event){var $$a=_vm.hideFollows,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.hideFollows=$$a.concat([$$v]))}else{$$i>-1&&(_vm.hideFollows=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.hideFollows=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"account-hide-follows\"}},[_vm._v(_vm._s(_vm.$t('settings.hide_follows_description')))])]),_vm._v(\" \"),_c('p',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.hideFollowers),expression:\"hideFollowers\"}],attrs:{\"id\":\"account-hide-followers\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.hideFollowers)?_vm._i(_vm.hideFollowers,null)>-1:(_vm.hideFollowers)},on:{\"change\":function($event){var $$a=_vm.hideFollowers,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.hideFollowers=$$a.concat([$$v]))}else{$$i>-1&&(_vm.hideFollowers=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.hideFollowers=$$c}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"account-hide-followers\"}},[_vm._v(_vm._s(_vm.$t('settings.hide_followers_description')))])]),_vm._v(\" \"),_c('p',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.showRole),expression:\"showRole\"}],attrs:{\"id\":\"account-show-role\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.showRole)?_vm._i(_vm.showRole,null)>-1:(_vm.showRole)},on:{\"change\":function($event){var $$a=_vm.showRole,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.showRole=$$a.concat([$$v]))}else{$$i>-1&&(_vm.showRole=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.showRole=$$c}}}}),_vm._v(\" \"),(_vm.role === 'admin')?_c('label',{attrs:{\"for\":\"account-show-role\"}},[_vm._v(_vm._s(_vm.$t('settings.show_admin_badge')))]):_vm._e(),_vm._v(\" \"),(_vm.role === 'moderator')?_c('label',{attrs:{\"for\":\"account-show-role\"}},[_vm._v(_vm._s(_vm.$t('settings.show_moderator_badge')))]):_vm._e()]),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-default\",attrs:{\"disabled\":_vm.newName && _vm.newName.length === 0},on:{\"click\":_vm.updateProfile}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.submit'))+\"\\n \")])],1),_vm._v(\" \"),_c('div',{staticClass:\"setting-item\"},[_c('h2',[_vm._v(_vm._s(_vm.$t('settings.avatar')))]),_vm._v(\" \"),_c('p',{staticClass:\"visibility-notice\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.avatar_size_instruction'))+\"\\n \")]),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.current_avatar')))]),_vm._v(\" \"),_c('img',{staticClass:\"current-avatar\",attrs:{\"src\":_vm.user.profile_image_url_original}}),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.set_new_avatar')))]),_vm._v(\" \"),_c('button',{directives:[{name:\"show\",rawName:\"v-show\",value:(_vm.pickAvatarBtnVisible),expression:\"pickAvatarBtnVisible\"}],staticClass:\"btn\",attrs:{\"id\":\"pick-avatar\",\"type\":\"button\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.upload_a_photo'))+\"\\n \")]),_vm._v(\" \"),_c('image-cropper',{attrs:{\"trigger\":\"#pick-avatar\",\"submit-handler\":_vm.submitAvatar},on:{\"open\":function($event){_vm.pickAvatarBtnVisible=false},\"close\":function($event){_vm.pickAvatarBtnVisible=true}}})],1),_vm._v(\" \"),_c('div',{staticClass:\"setting-item\"},[_c('h2',[_vm._v(_vm._s(_vm.$t('settings.profile_banner')))]),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.current_profile_banner')))]),_vm._v(\" \"),_c('img',{staticClass:\"banner\",attrs:{\"src\":_vm.user.cover_photo}}),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.set_new_profile_banner')))]),_vm._v(\" \"),(_vm.bannerPreview)?_c('img',{staticClass:\"banner\",attrs:{\"src\":_vm.bannerPreview}}):_vm._e(),_vm._v(\" \"),_c('div',[_c('input',{attrs:{\"type\":\"file\"},on:{\"change\":function($event){_vm.uploadFile('banner', $event)}}})]),_vm._v(\" \"),(_vm.bannerUploading)?_c('i',{staticClass:\" icon-spin4 animate-spin uploading\"}):(_vm.bannerPreview)?_c('button',{staticClass:\"btn btn-default\",on:{\"click\":_vm.submitBanner}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.submit'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(_vm.bannerUploadError)?_c('div',{staticClass:\"alert error\"},[_vm._v(\"\\n Error: \"+_vm._s(_vm.bannerUploadError)+\"\\n \"),_c('i',{staticClass:\"button-icon icon-cancel\",on:{\"click\":function($event){_vm.clearUploadError('banner')}}})]):_vm._e()]),_vm._v(\" \"),_c('div',{staticClass:\"setting-item\"},[_c('h2',[_vm._v(_vm._s(_vm.$t('settings.profile_background')))]),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.set_new_profile_background')))]),_vm._v(\" \"),(_vm.backgroundPreview)?_c('img',{staticClass:\"bg\",attrs:{\"src\":_vm.backgroundPreview}}):_vm._e(),_vm._v(\" \"),_c('div',[_c('input',{attrs:{\"type\":\"file\"},on:{\"change\":function($event){_vm.uploadFile('background', $event)}}})]),_vm._v(\" \"),(_vm.backgroundUploading)?_c('i',{staticClass:\" icon-spin4 animate-spin uploading\"}):(_vm.backgroundPreview)?_c('button',{staticClass:\"btn btn-default\",on:{\"click\":_vm.submitBg}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.submit'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(_vm.backgroundUploadError)?_c('div',{staticClass:\"alert error\"},[_vm._v(\"\\n Error: \"+_vm._s(_vm.backgroundUploadError)+\"\\n \"),_c('i',{staticClass:\"button-icon icon-cancel\",on:{\"click\":function($event){_vm.clearUploadError('background')}}})]):_vm._e()])]),_vm._v(\" \"),_c('div',{attrs:{\"label\":_vm.$t('settings.security_tab')}},[_c('div',{staticClass:\"setting-item\"},[_c('h2',[_vm._v(_vm._s(_vm.$t('settings.change_password')))]),_vm._v(\" \"),_c('div',[_c('p',[_vm._v(_vm._s(_vm.$t('settings.current_password')))]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.changePasswordInputs[0]),expression:\"changePasswordInputs[0]\"}],attrs:{\"type\":\"password\"},domProps:{\"value\":(_vm.changePasswordInputs[0])},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.changePasswordInputs, 0, $event.target.value)}}})]),_vm._v(\" \"),_c('div',[_c('p',[_vm._v(_vm._s(_vm.$t('settings.new_password')))]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.changePasswordInputs[1]),expression:\"changePasswordInputs[1]\"}],attrs:{\"type\":\"password\"},domProps:{\"value\":(_vm.changePasswordInputs[1])},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.changePasswordInputs, 1, $event.target.value)}}})]),_vm._v(\" \"),_c('div',[_c('p',[_vm._v(_vm._s(_vm.$t('settings.confirm_new_password')))]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.changePasswordInputs[2]),expression:\"changePasswordInputs[2]\"}],attrs:{\"type\":\"password\"},domProps:{\"value\":(_vm.changePasswordInputs[2])},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.changePasswordInputs, 2, $event.target.value)}}})]),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-default\",on:{\"click\":_vm.changePassword}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.submit'))+\"\\n \")]),_vm._v(\" \"),(_vm.changedPassword)?_c('p',[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.changed_password'))+\"\\n \")]):(_vm.changePasswordError !== false)?_c('p',[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.change_password_error'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(_vm.changePasswordError)?_c('p',[_vm._v(\"\\n \"+_vm._s(_vm.changePasswordError)+\"\\n \")]):_vm._e()]),_vm._v(\" \"),_c('div',{staticClass:\"setting-item\"},[_c('h2',[_vm._v(_vm._s(_vm.$t('settings.oauth_tokens')))]),_vm._v(\" \"),_c('table',{staticClass:\"oauth-tokens\"},[_c('thead',[_c('tr',[_c('th',[_vm._v(_vm._s(_vm.$t('settings.app_name')))]),_vm._v(\" \"),_c('th',[_vm._v(_vm._s(_vm.$t('settings.valid_until')))]),_vm._v(\" \"),_c('th')])]),_vm._v(\" \"),_c('tbody',_vm._l((_vm.oauthTokens),function(oauthToken){return _c('tr',{key:oauthToken.id},[_c('td',[_vm._v(_vm._s(oauthToken.appName))]),_vm._v(\" \"),_c('td',[_vm._v(_vm._s(oauthToken.validUntil))]),_vm._v(\" \"),_c('td',{staticClass:\"actions\"},[_c('button',{staticClass:\"btn btn-default\",on:{\"click\":function($event){_vm.revokeToken(oauthToken.id)}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.revoke_token'))+\"\\n \")])])])}),0)])]),_vm._v(\" \"),_c('mfa'),_vm._v(\" \"),_c('div',{staticClass:\"setting-item\"},[_c('h2',[_vm._v(_vm._s(_vm.$t('settings.delete_account')))]),_vm._v(\" \"),(!_vm.deletingAccount)?_c('p',[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.delete_account_description'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(_vm.deletingAccount)?_c('div',[_c('p',[_vm._v(_vm._s(_vm.$t('settings.delete_account_instructions')))]),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('login.password')))]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.deleteAccountConfirmPasswordInput),expression:\"deleteAccountConfirmPasswordInput\"}],attrs:{\"type\":\"password\"},domProps:{\"value\":(_vm.deleteAccountConfirmPasswordInput)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.deleteAccountConfirmPasswordInput=$event.target.value}}}),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-default\",on:{\"click\":_vm.deleteAccount}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.delete_account'))+\"\\n \")])]):_vm._e(),_vm._v(\" \"),(_vm.deleteAccountError !== false)?_c('p',[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.delete_account_error'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(_vm.deleteAccountError)?_c('p',[_vm._v(\"\\n \"+_vm._s(_vm.deleteAccountError)+\"\\n \")]):_vm._e(),_vm._v(\" \"),(!_vm.deletingAccount)?_c('button',{staticClass:\"btn btn-default\",on:{\"click\":_vm.confirmDelete}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.submit'))+\"\\n \")]):_vm._e()])],1),_vm._v(\" \"),(_vm.pleromaBackend)?_c('div',{attrs:{\"label\":_vm.$t('settings.notifications')}},[_c('div',{staticClass:\"setting-item\"},[_c('div',{staticClass:\"select-multiple\"},[_c('span',{staticClass:\"label\"},[_vm._v(_vm._s(_vm.$t('settings.notification_setting')))]),_vm._v(\" \"),_c('ul',{staticClass:\"option-list\"},[_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.notificationSettings.follows),expression:\"notificationSettings.follows\"}],attrs:{\"id\":\"notification-setting-follows\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.notificationSettings.follows)?_vm._i(_vm.notificationSettings.follows,null)>-1:(_vm.notificationSettings.follows)},on:{\"change\":function($event){var $$a=_vm.notificationSettings.follows,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.$set(_vm.notificationSettings, \"follows\", $$a.concat([$$v])))}else{$$i>-1&&(_vm.$set(_vm.notificationSettings, \"follows\", $$a.slice(0,$$i).concat($$a.slice($$i+1))))}}else{_vm.$set(_vm.notificationSettings, \"follows\", $$c)}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"notification-setting-follows\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.notification_setting_follows'))+\"\\n \")])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.notificationSettings.followers),expression:\"notificationSettings.followers\"}],attrs:{\"id\":\"notification-setting-followers\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.notificationSettings.followers)?_vm._i(_vm.notificationSettings.followers,null)>-1:(_vm.notificationSettings.followers)},on:{\"change\":function($event){var $$a=_vm.notificationSettings.followers,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.$set(_vm.notificationSettings, \"followers\", $$a.concat([$$v])))}else{$$i>-1&&(_vm.$set(_vm.notificationSettings, \"followers\", $$a.slice(0,$$i).concat($$a.slice($$i+1))))}}else{_vm.$set(_vm.notificationSettings, \"followers\", $$c)}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"notification-setting-followers\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.notification_setting_followers'))+\"\\n \")])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.notificationSettings.non_follows),expression:\"notificationSettings.non_follows\"}],attrs:{\"id\":\"notification-setting-non-follows\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.notificationSettings.non_follows)?_vm._i(_vm.notificationSettings.non_follows,null)>-1:(_vm.notificationSettings.non_follows)},on:{\"change\":function($event){var $$a=_vm.notificationSettings.non_follows,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.$set(_vm.notificationSettings, \"non_follows\", $$a.concat([$$v])))}else{$$i>-1&&(_vm.$set(_vm.notificationSettings, \"non_follows\", $$a.slice(0,$$i).concat($$a.slice($$i+1))))}}else{_vm.$set(_vm.notificationSettings, \"non_follows\", $$c)}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"notification-setting-non-follows\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.notification_setting_non_follows'))+\"\\n \")])]),_vm._v(\" \"),_c('li',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.notificationSettings.non_followers),expression:\"notificationSettings.non_followers\"}],attrs:{\"id\":\"notification-setting-non-followers\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.notificationSettings.non_followers)?_vm._i(_vm.notificationSettings.non_followers,null)>-1:(_vm.notificationSettings.non_followers)},on:{\"change\":function($event){var $$a=_vm.notificationSettings.non_followers,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.$set(_vm.notificationSettings, \"non_followers\", $$a.concat([$$v])))}else{$$i>-1&&(_vm.$set(_vm.notificationSettings, \"non_followers\", $$a.slice(0,$$i).concat($$a.slice($$i+1))))}}else{_vm.$set(_vm.notificationSettings, \"non_followers\", $$c)}}}}),_vm._v(\" \"),_c('label',{attrs:{\"for\":\"notification-setting-non-followers\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.notification_setting_non_followers'))+\"\\n \")])])])]),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.notification_mutes')))]),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.notification_blocks')))]),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-default\",on:{\"click\":_vm.updateNotificationSettings}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.submit'))+\"\\n \")])])]):_vm._e(),_vm._v(\" \"),(_vm.pleromaBackend)?_c('div',{attrs:{\"label\":_vm.$t('settings.data_import_export_tab')}},[_c('div',{staticClass:\"setting-item\"},[_c('h2',[_vm._v(_vm._s(_vm.$t('settings.follow_import')))]),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.import_followers_from_a_csv_file')))]),_vm._v(\" \"),_c('Importer',{attrs:{\"submit-handler\":_vm.importFollows,\"success-message\":_vm.$t('settings.follows_imported'),\"error-message\":_vm.$t('settings.follow_import_error')}})],1),_vm._v(\" \"),_c('div',{staticClass:\"setting-item\"},[_c('h2',[_vm._v(_vm._s(_vm.$t('settings.follow_export')))]),_vm._v(\" \"),_c('Exporter',{attrs:{\"get-content\":_vm.getFollowsContent,\"filename\":\"friends.csv\",\"export-button-label\":_vm.$t('settings.follow_export_button')}})],1),_vm._v(\" \"),_c('div',{staticClass:\"setting-item\"},[_c('h2',[_vm._v(_vm._s(_vm.$t('settings.block_import')))]),_vm._v(\" \"),_c('p',[_vm._v(_vm._s(_vm.$t('settings.import_blocks_from_a_csv_file')))]),_vm._v(\" \"),_c('Importer',{attrs:{\"submit-handler\":_vm.importBlocks,\"success-message\":_vm.$t('settings.blocks_imported'),\"error-message\":_vm.$t('settings.block_import_error')}})],1),_vm._v(\" \"),_c('div',{staticClass:\"setting-item\"},[_c('h2',[_vm._v(_vm._s(_vm.$t('settings.block_export')))]),_vm._v(\" \"),_c('Exporter',{attrs:{\"get-content\":_vm.getBlocksContent,\"filename\":\"blocks.csv\",\"export-button-label\":_vm.$t('settings.block_export_button')}})],1)]):_vm._e(),_vm._v(\" \"),_c('div',{attrs:{\"label\":_vm.$t('settings.blocks_tab')}},[_c('div',{staticClass:\"profile-edit-usersearch-wrapper\"},[_c('Autosuggest',{attrs:{\"filter\":_vm.filterUnblockedUsers,\"query\":_vm.queryUserIds,\"placeholder\":_vm.$t('settings.search_user_to_block')},scopedSlots:_vm._u([{key:\"default\",fn:function(row){return _c('BlockCard',{attrs:{\"user-id\":row.item}})}}])})],1),_vm._v(\" \"),_c('BlockList',{attrs:{\"refresh\":true,\"get-key\":_vm.identity},scopedSlots:_vm._u([{key:\"header\",fn:function(ref){\nvar selected = ref.selected;\nreturn [_c('div',{staticClass:\"profile-edit-bulk-actions\"},[(selected.length > 0)?_c('ProgressButton',{staticClass:\"btn btn-default\",attrs:{\"click\":function () { return _vm.blockUsers(selected); }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.block'))+\"\\n \"),_c('template',{slot:\"progress\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.block_progress'))+\"\\n \")])],2):_vm._e(),_vm._v(\" \"),(selected.length > 0)?_c('ProgressButton',{staticClass:\"btn btn-default\",attrs:{\"click\":function () { return _vm.unblockUsers(selected); }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.unblock'))+\"\\n \"),_c('template',{slot:\"progress\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.unblock_progress'))+\"\\n \")])],2):_vm._e()],1)]}},{key:\"item\",fn:function(ref){\nvar item = ref.item;\nreturn [_c('BlockCard',{attrs:{\"user-id\":item}})]}}])},[_c('template',{slot:\"empty\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.no_blocks'))+\"\\n \")])],2)],1),_vm._v(\" \"),_c('div',{attrs:{\"label\":_vm.$t('settings.mutes_tab')}},[_c('div',{staticClass:\"profile-edit-usersearch-wrapper\"},[_c('Autosuggest',{attrs:{\"filter\":_vm.filterUnMutedUsers,\"query\":_vm.queryUserIds,\"placeholder\":_vm.$t('settings.search_user_to_mute')},scopedSlots:_vm._u([{key:\"default\",fn:function(row){return _c('MuteCard',{attrs:{\"user-id\":row.item}})}}])})],1),_vm._v(\" \"),_c('MuteList',{attrs:{\"refresh\":true,\"get-key\":_vm.identity},scopedSlots:_vm._u([{key:\"header\",fn:function(ref){\nvar selected = ref.selected;\nreturn [_c('div',{staticClass:\"profile-edit-bulk-actions\"},[(selected.length > 0)?_c('ProgressButton',{staticClass:\"btn btn-default\",attrs:{\"click\":function () { return _vm.muteUsers(selected); }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.mute'))+\"\\n \"),_c('template',{slot:\"progress\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.mute_progress'))+\"\\n \")])],2):_vm._e(),_vm._v(\" \"),(selected.length > 0)?_c('ProgressButton',{staticClass:\"btn btn-default\",attrs:{\"click\":function () { return _vm.unmuteUsers(selected); }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.unmute'))+\"\\n \"),_c('template',{slot:\"progress\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.unmute_progress'))+\"\\n \")])],2):_vm._e()],1)]}},{key:\"item\",fn:function(ref){\nvar item = ref.item;\nreturn [_c('MuteCard',{attrs:{\"user-id\":item}})]}}])},[_c('template',{slot:\"empty\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('settings.no_mutes'))+\"\\n \")])],2)],1)])],1)])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('basic-user-card',{attrs:{\"user\":_vm.user}},[_c('div',{staticClass:\"follow-request-card-content-container\"},[_c('button',{staticClass:\"btn btn-default\",on:{\"click\":_vm.approveUser}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.approve'))+\"\\n \")]),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-default\",on:{\"click\":_vm.denyUser}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_card.deny'))+\"\\n \")])])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"settings panel panel-default\"},[_c('div',{staticClass:\"panel-heading\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('nav.friend_requests'))+\"\\n \")]),_vm._v(\" \"),_c('div',{staticClass:\"panel-body\"},_vm._l((_vm.requests),function(request){return _c('FollowRequestCard',{key:request.id,staticClass:\"list-item\",attrs:{\"user\":request}})}),1)])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('h1',[_vm._v(\"...\")])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"login panel panel-default\"},[_c('div',{staticClass:\"panel-heading\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('login.login'))+\"\\n \")]),_vm._v(\" \"),_c('div',{staticClass:\"panel-body\"},[_c('form',{staticClass:\"login-form\",on:{\"submit\":function($event){$event.preventDefault();return _vm.submit($event)}}},[(_vm.isPasswordAuth)?[_c('div',{staticClass:\"form-group\"},[_c('label',{attrs:{\"for\":\"username\"}},[_vm._v(_vm._s(_vm.$t('login.username')))]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.user.username),expression:\"user.username\"}],staticClass:\"form-control\",attrs:{\"id\":\"username\",\"disabled\":_vm.loggingIn,\"placeholder\":_vm.$t('login.placeholder')},domProps:{\"value\":(_vm.user.username)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.user, \"username\", $event.target.value)}}})]),_vm._v(\" \"),_c('div',{staticClass:\"form-group\"},[_c('label',{attrs:{\"for\":\"password\"}},[_vm._v(_vm._s(_vm.$t('login.password')))]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.user.password),expression:\"user.password\"}],ref:\"passwordInput\",staticClass:\"form-control\",attrs:{\"id\":\"password\",\"disabled\":_vm.loggingIn,\"type\":\"password\"},domProps:{\"value\":(_vm.user.password)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.user, \"password\", $event.target.value)}}})]),_vm._v(\" \"),_c('div',{staticClass:\"form-group\"},[_c('router-link',{attrs:{\"to\":{name: 'password-reset'}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('password_reset.forgot_password'))+\"\\n \")])],1)]:_vm._e(),_vm._v(\" \"),(_vm.isTokenAuth)?_c('div',{staticClass:\"form-group\"},[_c('p',[_vm._v(_vm._s(_vm.$t('login.description')))])]):_vm._e(),_vm._v(\" \"),_c('div',{staticClass:\"form-group\"},[_c('div',{staticClass:\"login-bottom\"},[_c('div',[(_vm.registrationOpen)?_c('router-link',{staticClass:\"register\",attrs:{\"to\":{name: 'registration'}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('login.register'))+\"\\n \")]):_vm._e()],1),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-default\",attrs:{\"disabled\":_vm.loggingIn,\"type\":\"submit\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('login.login'))+\"\\n \")])])])],2)]),_vm._v(\" \"),(_vm.error)?_c('div',{staticClass:\"form-group\"},[_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()])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"login panel panel-default\"},[_c('div',{staticClass:\"panel-heading\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('login.heading.recovery'))+\"\\n \")]),_vm._v(\" \"),_c('div',{staticClass:\"panel-body\"},[_c('form',{staticClass:\"login-form\",on:{\"submit\":function($event){$event.preventDefault();return _vm.submit($event)}}},[_c('div',{staticClass:\"form-group\"},[_c('label',{attrs:{\"for\":\"code\"}},[_vm._v(_vm._s(_vm.$t('login.recovery_code')))]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.code),expression:\"code\"}],staticClass:\"form-control\",attrs:{\"id\":\"code\"},domProps:{\"value\":(_vm.code)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.code=$event.target.value}}})]),_vm._v(\" \"),_c('div',{staticClass:\"form-group\"},[_c('div',{staticClass:\"login-bottom\"},[_c('div',[_c('a',{attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();return _vm.requireTOTP($event)}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('login.enter_two_factor_code'))+\"\\n \")]),_vm._v(\" \"),_c('br'),_vm._v(\" \"),_c('a',{attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();return _vm.abortMFA($event)}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.cancel'))+\"\\n \")])]),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-default\",attrs:{\"type\":\"submit\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.verify'))+\"\\n \")])])])])]),_vm._v(\" \"),(_vm.error)?_c('div',{staticClass:\"form-group\"},[_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()])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"login panel panel-default\"},[_c('div',{staticClass:\"panel-heading\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('login.heading.totp'))+\"\\n \")]),_vm._v(\" \"),_c('div',{staticClass:\"panel-body\"},[_c('form',{staticClass:\"login-form\",on:{\"submit\":function($event){$event.preventDefault();return _vm.submit($event)}}},[_c('div',{staticClass:\"form-group\"},[_c('label',{attrs:{\"for\":\"code\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('login.authentication_code'))+\"\\n \")]),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.code),expression:\"code\"}],staticClass:\"form-control\",attrs:{\"id\":\"code\"},domProps:{\"value\":(_vm.code)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.code=$event.target.value}}})]),_vm._v(\" \"),_c('div',{staticClass:\"form-group\"},[_c('div',{staticClass:\"login-bottom\"},[_c('div',[_c('a',{attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();return _vm.requireRecovery($event)}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('login.enter_recovery_code'))+\"\\n \")]),_vm._v(\" \"),_c('br'),_vm._v(\" \"),_c('a',{attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();return _vm.abortMFA($event)}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.cancel'))+\"\\n \")])]),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-default\",attrs:{\"type\":\"submit\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('general.verify'))+\"\\n \")])])])])]),_vm._v(\" \"),(_vm.error)?_c('div',{staticClass:\"form-group\"},[_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()])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return (!_vm.collapsed || !_vm.floating)?_c('div',{staticClass:\"chat-panel\"},[_c('div',{staticClass:\"panel panel-default\"},[_c('div',{staticClass:\"panel-heading timeline-heading\",class:{ 'chat-heading': _vm.floating },on:{\"click\":function($event){$event.stopPropagation();$event.preventDefault();return _vm.togglePanel($event)}}},[_c('div',{staticClass:\"title\"},[_c('span',[_vm._v(_vm._s(_vm.$t('chat.title')))]),_vm._v(\" \"),(_vm.floating)?_c('i',{staticClass:\"icon-cancel\"}):_vm._e()])]),_vm._v(\" \"),_c('div',{directives:[{name:\"chat-scroll\",rawName:\"v-chat-scroll\"}],staticClass:\"chat-window\"},_vm._l((_vm.messages),function(message){return _c('div',{key:message.id,staticClass:\"chat-message\"},[_c('span',{staticClass:\"chat-avatar\"},[_c('img',{attrs:{\"src\":message.author.avatar}})]),_vm._v(\" \"),_c('div',{staticClass:\"chat-content\"},[_c('router-link',{staticClass:\"chat-name\",attrs:{\"to\":_vm.userProfileLink(message.author)}},[_vm._v(\"\\n \"+_vm._s(message.author.username)+\"\\n \")]),_vm._v(\" \"),_c('br'),_vm._v(\" \"),_c('span',{staticClass:\"chat-text\"},[_vm._v(\"\\n \"+_vm._s(message.text)+\"\\n \")])],1)])}),0),_vm._v(\" \"),_c('div',{staticClass:\"chat-input\"},[_c('textarea',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.currentMessage),expression:\"currentMessage\"}],staticClass:\"chat-input-textarea\",attrs:{\"rows\":\"1\"},domProps:{\"value\":(_vm.currentMessage)},on:{\"keyup\":function($event){if(!('button' in $event)&&_vm._k($event.keyCode,\"enter\",13,$event.key,\"Enter\")){ return null; }_vm.submit(_vm.currentMessage)},\"input\":function($event){if($event.target.composing){ return; }_vm.currentMessage=$event.target.value}}})])])]):_c('div',{staticClass:\"chat-panel\"},[_c('div',{staticClass:\"panel panel-default\"},[_c('div',{staticClass:\"panel-heading stub timeline-heading chat-heading\",on:{\"click\":function($event){$event.stopPropagation();$event.preventDefault();return _vm.togglePanel($event)}}},[_c('div',{staticClass:\"title\"},[_c('i',{staticClass:\"icon-comment-empty\"}),_vm._v(\"\\n \"+_vm._s(_vm.$t('chat.title'))+\"\\n \")])])])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"panel panel-default\"},[_c('div',{staticClass:\"panel-heading\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('who_to_follow.who_to_follow'))+\"\\n \")]),_vm._v(\" \"),_c('div',{staticClass:\"panel-body\"},_vm._l((_vm.users),function(user){return _c('FollowCard',{key:user.id,staticClass:\"list-item\",attrs:{\"user\":user}})}),1)])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"instance-specific-panel\"},[_c('div',{staticClass:\"panel panel-default\"},[_c('div',{staticClass:\"panel-body\"},[_c('div',{domProps:{\"innerHTML\":_vm._s(_vm.instanceSpecificPanelContent)}})])])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"features-panel\"},[_c('div',{staticClass:\"panel panel-default base01-background\"},[_c('div',{staticClass:\"panel-heading timeline-heading base02-background base04\"},[_c('div',{staticClass:\"title\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('features_panel.title'))+\"\\n \")])]),_vm._v(\" \"),_c('div',{staticClass:\"panel-body features-panel\"},[_c('ul',[(_vm.chat)?_c('li',[_vm._v(\"\\n \"+_vm._s(_vm.$t('features_panel.chat'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(_vm.gopher)?_c('li',[_vm._v(\"\\n \"+_vm._s(_vm.$t('features_panel.gopher'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(_vm.whoToFollow)?_c('li',[_vm._v(\"\\n \"+_vm._s(_vm.$t('features_panel.who_to_follow'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(_vm.mediaProxy)?_c('li',[_vm._v(\"\\n \"+_vm._s(_vm.$t('features_panel.media_proxy'))+\"\\n \")]):_vm._e(),_vm._v(\" \"),_c('li',[_vm._v(_vm._s(_vm.$t('features_panel.scope_options')))]),_vm._v(\" \"),_c('li',[_vm._v(_vm._s(_vm.$t('features_panel.text_limit'))+\" = \"+_vm._s(_vm.textlimit))])])])])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('div',{staticClass:\"panel panel-default\"},[_c('div',{staticClass:\"panel-body\"},[_c('div',{staticClass:\"tos-content\",domProps:{\"innerHTML\":_vm._s(_vm.content)}})])])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"sidebar\"},[_c('instance-specific-panel'),_vm._v(\" \"),(_vm.showFeaturesPanel)?_c('features-panel'):_vm._e(),_vm._v(\" \"),_c('terms-of-service-panel')],1)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"user-panel\"},[(_vm.signedIn)?_c('div',{key:\"user-panel\",staticClass:\"panel panel-default signed-in\"},[_c('UserCard',{attrs:{\"user\":_vm.user,\"hide-bio\":true,\"rounded\":\"top\"}}),_vm._v(\" \"),_c('div',{staticClass:\"panel-footer\"},[(_vm.user)?_c('post-status-form'):_vm._e()],1)],1):_c('auth-form',{key:\"user-panel\"})],1)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"nav-panel\"},[_c('div',{staticClass:\"panel panel-default\"},[_c('ul',[(_vm.currentUser)?_c('li',[_c('router-link',{attrs:{\"to\":{ name: 'friends' }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"nav.timeline\"))+\"\\n \")])],1):_vm._e(),_vm._v(\" \"),(_vm.currentUser)?_c('li',[_c('router-link',{attrs:{\"to\":{ name: 'interactions', params: { username: _vm.currentUser.screen_name } }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"nav.interactions\"))+\"\\n \")])],1):_vm._e(),_vm._v(\" \"),(_vm.currentUser)?_c('li',[_c('router-link',{attrs:{\"to\":{ name: 'dms', params: { username: _vm.currentUser.screen_name } }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"nav.dms\"))+\"\\n \")])],1):_vm._e(),_vm._v(\" \"),(_vm.currentUser && _vm.currentUser.locked)?_c('li',[_c('router-link',{attrs:{\"to\":{ name: 'friend-requests' }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"nav.friend_requests\"))+\"\\n \"),(_vm.followRequestCount > 0)?_c('span',{staticClass:\"badge follow-request-count\"},[_vm._v(\"\\n \"+_vm._s(_vm.followRequestCount)+\"\\n \")]):_vm._e()])],1):_vm._e(),_vm._v(\" \"),_c('li',[_c('router-link',{attrs:{\"to\":{ name: 'public-timeline' }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"nav.public_tl\"))+\"\\n \")])],1),_vm._v(\" \"),_c('li',[_c('router-link',{attrs:{\"to\":{ name: 'public-external-timeline' }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"nav.twkn\"))+\"\\n \")])],1)])])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('div',{staticClass:\"search-bar-container\"},[(_vm.loading)?_c('i',{staticClass:\"icon-spin4 finder-icon animate-spin-slow\"}):_vm._e(),_vm._v(\" \"),(_vm.hidden)?_c('a',{attrs:{\"href\":\"#\",\"title\":_vm.$t('nav.search')}},[_c('i',{staticClass:\"button-icon icon-search\",on:{\"click\":function($event){$event.preventDefault();$event.stopPropagation();return _vm.toggleHidden($event)}}})]):[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.searchTerm),expression:\"searchTerm\"}],ref:\"searchInput\",staticClass:\"search-bar-input\",attrs:{\"id\":\"search-bar-input\",\"placeholder\":_vm.$t('nav.search'),\"type\":\"text\"},domProps:{\"value\":(_vm.searchTerm)},on:{\"keyup\":function($event){if(!('button' in $event)&&_vm._k($event.keyCode,\"enter\",13,$event.key,\"Enter\")){ return null; }_vm.find(_vm.searchTerm)},\"input\":function($event){if($event.target.composing){ return; }_vm.searchTerm=$event.target.value}}}),_vm._v(\" \"),_c('button',{staticClass:\"btn search-button\",on:{\"click\":function($event){_vm.find(_vm.searchTerm)}}},[_c('i',{staticClass:\"icon-search\"})]),_vm._v(\" \"),_c('i',{staticClass:\"button-icon icon-cancel\",on:{\"click\":function($event){$event.preventDefault();$event.stopPropagation();return _vm.toggleHidden($event)}}})]],2)])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"who-to-follow-panel\"},[_c('div',{staticClass:\"panel panel-default base01-background\"},[_c('div',{staticClass:\"panel-heading timeline-heading base02-background base04\"},[_c('div',{staticClass:\"title\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('who_to_follow.who_to_follow'))+\"\\n \")])]),_vm._v(\" \"),_c('div',{staticClass:\"who-to-follow\"},[_vm._l((_vm.usersToFollow),function(user){return _c('p',{key:user.id,staticClass:\"who-to-follow-items\"},[_c('img',{attrs:{\"src\":user.img}}),_vm._v(\" \"),_c('router-link',{attrs:{\"to\":_vm.userProfileLink(user.id, user.name)}},[_vm._v(\"\\n \"+_vm._s(user.name)+\"\\n \")]),_c('br')],1)}),_vm._v(\" \"),_c('p',{staticClass:\"who-to-follow-more\"},[_c('router-link',{attrs:{\"to\":{ name: 'who-to-follow' }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('who_to_follow.more'))+\"\\n \")])],1)],2)])])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return (_vm.showing)?_c('div',{staticClass:\"modal-view media-modal-view\",on:{\"click\":function($event){$event.preventDefault();return _vm.hide($event)}}},[(_vm.type === 'image')?_c('img',{staticClass:\"modal-image\",attrs:{\"src\":_vm.currentMedia.url}}):_vm._e(),_vm._v(\" \"),(_vm.type === 'video')?_c('VideoAttachment',{staticClass:\"modal-image\",attrs:{\"attachment\":_vm.currentMedia,\"controls\":true},nativeOn:{\"click\":function($event){$event.stopPropagation();}}}):_vm._e(),_vm._v(\" \"),(_vm.canNavigate)?_c('button',{staticClass:\"modal-view-button-arrow modal-view-button-arrow--prev\",attrs:{\"title\":_vm.$t('media_modal.previous')},on:{\"click\":function($event){$event.stopPropagation();$event.preventDefault();return _vm.goPrev($event)}}},[_c('i',{staticClass:\"icon-left-open arrow-icon\"})]):_vm._e(),_vm._v(\" \"),(_vm.canNavigate)?_c('button',{staticClass:\"modal-view-button-arrow modal-view-button-arrow--next\",attrs:{\"title\":_vm.$t('media_modal.next')},on:{\"click\":function($event){$event.stopPropagation();$event.preventDefault();return _vm.goNext($event)}}},[_c('i',{staticClass:\"icon-right-open arrow-icon\"})]):_vm._e()],1):_vm._e()}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"side-drawer-container\",class:{ 'side-drawer-container-closed': _vm.closed, 'side-drawer-container-open': !_vm.closed }},[_c('div',{staticClass:\"side-drawer-darken\",class:{ 'side-drawer-darken-closed': _vm.closed}}),_vm._v(\" \"),_c('div',{staticClass:\"side-drawer\",class:{'side-drawer-closed': _vm.closed},on:{\"touchstart\":_vm.touchStart,\"touchmove\":_vm.touchMove}},[_c('div',{staticClass:\"side-drawer-heading\",on:{\"click\":_vm.toggleDrawer}},[(_vm.currentUser)?_c('UserCard',{attrs:{\"user\":_vm.currentUser,\"hide-bio\":true}}):_c('div',{staticClass:\"side-drawer-logo-wrapper\"},[_c('img',{attrs:{\"src\":_vm.logo}}),_vm._v(\" \"),_c('span',[_vm._v(_vm._s(_vm.sitename))])])],1),_vm._v(\" \"),_c('ul',[(!_vm.currentUser)?_c('li',{on:{\"click\":_vm.toggleDrawer}},[_c('router-link',{attrs:{\"to\":{ name: 'login' }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"login.login\"))+\"\\n \")])],1):_vm._e(),_vm._v(\" \"),(_vm.currentUser)?_c('li',{on:{\"click\":_vm.toggleDrawer}},[_c('router-link',{attrs:{\"to\":{ name: 'dms', params: { username: _vm.currentUser.screen_name } }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"nav.dms\"))+\"\\n \")])],1):_vm._e(),_vm._v(\" \"),(_vm.currentUser)?_c('li',{on:{\"click\":_vm.toggleDrawer}},[_c('router-link',{attrs:{\"to\":{ name: 'interactions', params: { username: _vm.currentUser.screen_name } }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"nav.interactions\"))+\"\\n \")])],1):_vm._e()]),_vm._v(\" \"),_c('ul',[(_vm.currentUser)?_c('li',{on:{\"click\":_vm.toggleDrawer}},[_c('router-link',{attrs:{\"to\":{ name: 'friends' }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"nav.timeline\"))+\"\\n \")])],1):_vm._e(),_vm._v(\" \"),(_vm.currentUser && _vm.currentUser.locked)?_c('li',{on:{\"click\":_vm.toggleDrawer}},[_c('router-link',{attrs:{\"to\":\"/friend-requests\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"nav.friend_requests\"))+\"\\n \"),(_vm.followRequestCount > 0)?_c('span',{staticClass:\"badge follow-request-count\"},[_vm._v(\"\\n \"+_vm._s(_vm.followRequestCount)+\"\\n \")]):_vm._e()])],1):_vm._e(),_vm._v(\" \"),_c('li',{on:{\"click\":_vm.toggleDrawer}},[_c('router-link',{attrs:{\"to\":\"/main/public\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"nav.public_tl\"))+\"\\n \")])],1),_vm._v(\" \"),_c('li',{on:{\"click\":_vm.toggleDrawer}},[_c('router-link',{attrs:{\"to\":\"/main/all\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"nav.twkn\"))+\"\\n \")])],1),_vm._v(\" \"),(_vm.currentUser && _vm.chat)?_c('li',{on:{\"click\":_vm.toggleDrawer}},[_c('router-link',{attrs:{\"to\":{ name: 'chat' }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"nav.chat\"))+\"\\n \")])],1):_vm._e()]),_vm._v(\" \"),_c('ul',[_c('li',{on:{\"click\":_vm.toggleDrawer}},[_c('router-link',{attrs:{\"to\":{ name: 'search' }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"nav.search\"))+\"\\n \")])],1),_vm._v(\" \"),(_vm.currentUser && _vm.suggestionsEnabled)?_c('li',{on:{\"click\":_vm.toggleDrawer}},[_c('router-link',{attrs:{\"to\":{ name: 'who-to-follow' }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"nav.who_to_follow\"))+\"\\n \")])],1):_vm._e(),_vm._v(\" \"),_c('li',{on:{\"click\":_vm.toggleDrawer}},[_c('router-link',{attrs:{\"to\":{ name: 'settings' }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"settings.settings\"))+\"\\n \")])],1),_vm._v(\" \"),_c('li',{on:{\"click\":_vm.toggleDrawer}},[_c('router-link',{attrs:{\"to\":{ name: 'about'}}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"nav.about\"))+\"\\n \")])],1),_vm._v(\" \"),(_vm.currentUser)?_c('li',{on:{\"click\":_vm.toggleDrawer}},[_c('a',{attrs:{\"href\":\"#\"},on:{\"click\":_vm.doLogout}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"login.logout\"))+\"\\n \")])]):_vm._e()])]),_vm._v(\" \"),_c('div',{staticClass:\"side-drawer-click-outside\",class:{'side-drawer-click-outside-closed': _vm.closed},on:{\"click\":function($event){$event.stopPropagation();$event.preventDefault();return _vm.toggleDrawer($event)}}})])}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return (_vm.currentUser)?_c('div',[_c('div',{directives:[{name:\"show\",rawName:\"v-show\",value:(_vm.postFormOpen),expression:\"postFormOpen\"}],staticClass:\"post-form-modal-view modal-view\",on:{\"click\":_vm.closePostForm}},[_c('div',{staticClass:\"post-form-modal-panel panel\",on:{\"click\":function($event){$event.stopPropagation();}}},[_c('div',{staticClass:\"panel-heading\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('post_status.new_status'))+\"\\n \")]),_vm._v(\" \"),_c('PostStatusForm',{staticClass:\"panel-body\",on:{\"posted\":_vm.closePostForm}})],1)]),_vm._v(\" \"),_c('button',{staticClass:\"new-status-button\",class:{ 'hidden': _vm.isHidden },on:{\"click\":_vm.openPostForm}},[_c('i',{staticClass:\"icon-edit\"})])]):_vm._e()}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('nav',{staticClass:\"nav-bar container\",attrs:{\"id\":\"nav\"}},[_c('div',{staticClass:\"mobile-inner-nav\",on:{\"click\":function($event){_vm.scrollToTop()}}},[_c('div',{staticClass:\"item\"},[_c('a',{staticClass:\"mobile-nav-button\",attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.stopPropagation();$event.preventDefault();_vm.toggleMobileSidebar()}}},[_c('i',{staticClass:\"button-icon icon-menu\"})]),_vm._v(\" \"),_c('router-link',{staticClass:\"site-name\",attrs:{\"to\":{ name: 'root' },\"active-class\":\"home\"}},[_vm._v(\"\\n \"+_vm._s(_vm.sitename)+\"\\n \")])],1),_vm._v(\" \"),_c('div',{staticClass:\"item right\"},[(_vm.currentUser)?_c('a',{staticClass:\"mobile-nav-button\",attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.stopPropagation();$event.preventDefault();_vm.openMobileNotifications()}}},[_c('i',{staticClass:\"button-icon icon-bell-alt\"}),_vm._v(\" \"),(_vm.unseenNotificationsCount)?_c('div',{staticClass:\"alert-dot\"}):_vm._e()]):_vm._e()])])]),_vm._v(\" \"),(_vm.currentUser)?_c('div',{staticClass:\"mobile-notifications-drawer\",class:{ 'closed': !_vm.notificationsOpen },on:{\"touchstart\":function($event){$event.stopPropagation();return _vm.notificationsTouchStart($event)},\"touchmove\":function($event){$event.stopPropagation();return _vm.notificationsTouchMove($event)}}},[_c('div',{staticClass:\"mobile-notifications-header\"},[_c('span',{staticClass:\"title\"},[_vm._v(_vm._s(_vm.$t('notifications.notifications')))]),_vm._v(\" \"),_c('a',{staticClass:\"mobile-nav-button\",on:{\"click\":function($event){$event.stopPropagation();$event.preventDefault();_vm.closeMobileNotifications()}}},[_c('i',{staticClass:\"button-icon icon-cancel\"})])]),_vm._v(\" \"),_c('div',{staticClass:\"mobile-notifications\",on:{\"scroll\":_vm.onScroll}},[_c('Notifications',{ref:\"notifications\",attrs:{\"no-heading\":true}})],1)]):_vm._e(),_vm._v(\" \"),_c('SideDrawer',{ref:\"sideDrawer\",attrs:{\"logout\":_vm.logout}})],1)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return (_vm.isOpen)?_c('div',{staticClass:\"modal-view\",on:{\"click\":_vm.closeModal}},[_c('div',{staticClass:\"user-reporting-panel panel\",on:{\"click\":function($event){$event.stopPropagation();}}},[_c('div',{staticClass:\"panel-heading\"},[_c('div',{staticClass:\"title\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_reporting.title', [_vm.user.screen_name]))+\"\\n \")])]),_vm._v(\" \"),_c('div',{staticClass:\"panel-body\"},[_c('div',{staticClass:\"user-reporting-panel-left\"},[_c('div',[_c('p',[_vm._v(_vm._s(_vm.$t('user_reporting.add_comment_description')))]),_vm._v(\" \"),_c('textarea',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.comment),expression:\"comment\"}],staticClass:\"form-control\",attrs:{\"placeholder\":_vm.$t('user_reporting.additional_comments'),\"rows\":\"1\"},domProps:{\"value\":(_vm.comment)},on:{\"input\":[function($event){if($event.target.composing){ return; }_vm.comment=$event.target.value},_vm.resize]}})]),_vm._v(\" \"),(!_vm.user.is_local)?_c('div',[_c('p',[_vm._v(_vm._s(_vm.$t('user_reporting.forward_description')))]),_vm._v(\" \"),_c('Checkbox',{model:{value:(_vm.forward),callback:function ($$v) {_vm.forward=$$v},expression:\"forward\"}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_reporting.forward_to', [_vm.remoteInstance]))+\"\\n \")])],1):_vm._e(),_vm._v(\" \"),_c('div',[_c('button',{staticClass:\"btn btn-default\",attrs:{\"disabled\":_vm.processing},on:{\"click\":_vm.reportUser}},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_reporting.submit'))+\"\\n \")]),_vm._v(\" \"),(_vm.error)?_c('div',{staticClass:\"alert error\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('user_reporting.generic_error'))+\"\\n \")]):_vm._e()])]),_vm._v(\" \"),_c('div',{staticClass:\"user-reporting-panel-right\"},[_c('List',{attrs:{\"items\":_vm.statuses},scopedSlots:_vm._u([{key:\"item\",fn:function(ref){\nvar item = ref.item;\nreturn [_c('div',{staticClass:\"status-fadein user-reporting-panel-sitem\"},[_c('Status',{attrs:{\"in-conversation\":false,\"focused\":false,\"statusoid\":item}}),_vm._v(\" \"),_c('Checkbox',{attrs:{\"checked\":_vm.isChecked(item.id)},on:{\"change\":function (checked) { return _vm.toggleStatus(checked, item.id); }}})],1)]}}])})],1)])])]):_vm._e()}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{style:(_vm.bgAppStyle),attrs:{\"id\":\"app\"}},[_c('div',{staticClass:\"app-bg-wrapper\",style:(_vm.bgStyle)}),_vm._v(\" \"),(_vm.isMobileLayout)?_c('MobileNav'):_c('nav',{staticClass:\"nav-bar container\",attrs:{\"id\":\"nav\"},on:{\"click\":function($event){_vm.scrollToTop()}}},[_c('div',{staticClass:\"logo\",style:(_vm.logoBgStyle)},[_c('div',{staticClass:\"mask\",style:(_vm.logoMaskStyle)}),_vm._v(\" \"),_c('img',{style:(_vm.logoStyle),attrs:{\"src\":_vm.logo}})]),_vm._v(\" \"),_c('div',{staticClass:\"inner-nav\"},[_c('div',{staticClass:\"item\"},[_c('router-link',{staticClass:\"site-name\",attrs:{\"to\":{ name: 'root' },\"active-class\":\"home\"}},[_vm._v(\"\\n \"+_vm._s(_vm.sitename)+\"\\n \")])],1),_vm._v(\" \"),_c('div',{staticClass:\"item right\"},[_c('search-bar',{staticClass:\"nav-icon mobile-hidden\",on:{\"toggled\":_vm.onSearchBarToggled},nativeOn:{\"click\":function($event){$event.stopPropagation();}}}),_vm._v(\" \"),_c('router-link',{staticClass:\"mobile-hidden\",attrs:{\"to\":{ name: 'settings'}}},[_c('i',{staticClass:\"button-icon icon-cog nav-icon\",attrs:{\"title\":_vm.$t('nav.preferences')}})]),_vm._v(\" \"),(_vm.currentUser)?_c('a',{staticClass:\"mobile-hidden\",attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();return _vm.logout($event)}}},[_c('i',{staticClass:\"button-icon icon-logout nav-icon\",attrs:{\"title\":_vm.$t('login.logout')}})]):_vm._e()],1)])]),_vm._v(\" \"),_c('div',{staticClass:\"container\",attrs:{\"id\":\"content\"}},[_c('div',{staticClass:\"sidebar-flexer mobile-hidden\"},[_c('div',{staticClass:\"sidebar-bounds\"},[_c('div',{staticClass:\"sidebar-scroller\"},[_c('div',{staticClass:\"sidebar\"},[_c('user-panel'),_vm._v(\" \"),(!_vm.isMobileLayout)?_c('div',[_c('nav-panel'),_vm._v(\" \"),(_vm.showInstanceSpecificPanel)?_c('instance-specific-panel'):_vm._e(),_vm._v(\" \"),(!_vm.currentUser && _vm.showFeaturesPanel)?_c('features-panel'):_vm._e(),_vm._v(\" \"),(_vm.currentUser && _vm.suggestionsEnabled)?_c('who-to-follow-panel'):_vm._e(),_vm._v(\" \"),(_vm.currentUser)?_c('notifications'):_vm._e()],1):_vm._e()],1)])])]),_vm._v(\" \"),_c('div',{staticClass:\"main\"},[(!_vm.currentUser)?_c('div',{staticClass:\"login-hint panel panel-default\"},[_c('router-link',{staticClass:\"panel-body\",attrs:{\"to\":{ name: 'login' }}},[_vm._v(\"\\n \"+_vm._s(_vm.$t(\"login.hint\"))+\"\\n \")])],1):_vm._e(),_vm._v(\" \"),_c('transition',{attrs:{\"name\":\"fade\"}},[_c('router-view')],1)],1),_vm._v(\" \"),_c('media-modal')],1),_vm._v(\" \"),(_vm.currentUser && _vm.chat)?_c('chat-panel',{staticClass:\"floating-chat mobile-hidden\",attrs:{\"floating\":true}}):_vm._e(),_vm._v(\" \"),_c('MobilePostStatusModal'),_vm._v(\" \"),_c('UserReportingModal'),_vm._v(\" \"),_c('portal-target',{attrs:{\"name\":\"modal\"}})],1)}\nvar staticRenderFns = []\nexport { render, staticRenderFns }","import { camelCase } from 'lodash'\n\nimport apiService from '../api/api.service.js'\n\nconst update = ({ store, statuses, timeline, showImmediately, userId }) => {\n const ccTimeline = camelCase(timeline)\n\n store.dispatch('setError', { value: false })\n\n store.dispatch('addNewStatuses', {\n timeline: ccTimeline,\n userId,\n statuses,\n showImmediately\n })\n}\n\nconst fetchAndUpdate = ({ store, credentials, timeline = 'friends', older = false, showImmediately = false, userId = false, tag = false, until }) => {\n const args = { timeline, credentials }\n const rootState = store.rootState || store.state\n const timelineData = rootState.statuses.timelines[camelCase(timeline)]\n const hideMutedPosts = typeof rootState.config.hideMutedPosts === 'undefined'\n ? rootState.instance.hideMutedPosts\n : rootState.config.hideMutedPosts\n\n if (older) {\n args['until'] = until || timelineData.minId\n } else {\n args['since'] = timelineData.maxId\n }\n\n args['userId'] = userId\n args['tag'] = tag\n args['withMuted'] = !hideMutedPosts\n\n const numStatusesBeforeFetch = timelineData.statuses.length\n\n return apiService.fetchTimeline(args)\n .then((statuses) => {\n if (!older && statuses.length >= 20 && !timelineData.loading && numStatusesBeforeFetch > 0) {\n store.dispatch('queueFlush', { timeline: timeline, id: timelineData.maxId })\n }\n update({ store, statuses, timeline, showImmediately, userId })\n return statuses\n }, () => store.dispatch('setError', { value: true }))\n}\n\nconst startFetching = ({ timeline = 'friends', credentials, store, userId = false, tag = false }) => {\n const rootState = store.rootState || store.state\n const timelineData = rootState.statuses.timelines[camelCase(timeline)]\n const showImmediately = timelineData.visibleStatuses.length === 0\n timelineData.userId = userId\n fetchAndUpdate({ timeline, credentials, store, showImmediately, userId, tag })\n const boundFetchAndUpdate = () => fetchAndUpdate({ timeline, credentials, store, userId, tag })\n return setInterval(boundFetchAndUpdate, 10000)\n}\nconst timelineFetcher = {\n fetchAndUpdate,\n startFetching\n}\n\nexport default timelineFetcher\n","import apiService from '../api/api.service.js'\n\nconst update = ({ store, notifications, older }) => {\n store.dispatch('setNotificationsError', { value: false })\n\n store.dispatch('addNewNotifications', { notifications, older })\n}\n\nconst fetchAndUpdate = ({ store, credentials, older = false }) => {\n const args = { credentials }\n const rootState = store.rootState || store.state\n const timelineData = rootState.statuses.notifications\n\n args['timeline'] = 'notifications'\n if (older) {\n if (timelineData.minId !== Number.POSITIVE_INFINITY) {\n args['until'] = timelineData.minId\n }\n return fetchNotifications({ store, args, older })\n } else {\n // fetch new notifications\n if (timelineData.maxId !== Number.POSITIVE_INFINITY) {\n args['since'] = timelineData.maxId\n }\n const result = fetchNotifications({ store, args, older })\n\n // load unread notifications repeatedly to provide consistency between browser tabs\n const notifications = timelineData.data\n const unread = notifications.filter(n => !n.seen).map(n => n.id)\n if (unread.length) {\n args['since'] = Math.min(...unread)\n fetchNotifications({ store, args, older })\n }\n\n return result\n }\n}\n\nconst fetchNotifications = ({ store, args, older }) => {\n return apiService.fetchTimeline(args)\n .then((notifications) => {\n update({ store, notifications, older })\n return notifications\n }, () => store.dispatch('setNotificationsError', { value: true }))\n .catch(() => store.dispatch('setNotificationsError', { value: true }))\n}\n\nconst startFetching = ({ credentials, store }) => {\n fetchAndUpdate({ credentials, store })\n const boundFetchAndUpdate = () => fetchAndUpdate({ credentials, store })\n // Initially there's set flag to silence all desktop notifications so\n // that there won't spam of them when user just opened up the FE we\n // reset that flag after a while to show new notifications once again.\n setTimeout(() => store.dispatch('setNotificationsSilence', false), 10000)\n return setInterval(boundFetchAndUpdate, 10000)\n}\n\nconst notificationsFetcher = {\n fetchAndUpdate,\n startFetching\n}\n\nexport default notificationsFetcher\n","// When contributing, please sort JSON before committing so it would be easier to see what's missing and what's being added compared to English and other languages. It's not obligatory, but just an advice.\n// To sort json use jq https://stedolan.github.io/jq and invoke it like `jq -S . xx.json > xx.sorted.json`, AFAIK, there's no inplace edit option like in sed\n// Also, when adding a new language to \"messages\" variable, please do it alphabetically by language code so that users can search or check their custom language easily.\n\n// For anyone contributing to old huge messages.js and in need to quickly convert it to JSON\n// sed command for converting currently formatted JS to JSON:\n// sed -i -e \"s/'//gm\" -e 's/\"/\\\\\"/gm' -re 's/^( +)(.+?): ((.+?))?(,?)(\\{?)$/\\1\"\\2\": \"\\4\"/gm' -e 's/\\\"\\{\\\"/{/g' -e 's/,\"$/\",/g' file.json\n// There's only problem that apostrophe character ' gets replaced by \\\\ so you have to fix it manually, sorry.\n\nconst messages = {\n ar: require('./ar.json'),\n ca: require('./ca.json'),\n cs: require('./cs.json'),\n de: require('./de.json'),\n en: require('./en.json'),\n eo: require('./eo.json'),\n es: require('./es.json'),\n et: require('./et.json'),\n eu: require('./eu.json'),\n fi: require('./fi.json'),\n fr: require('./fr.json'),\n ga: require('./ga.json'),\n he: require('./he.json'),\n hu: require('./hu.json'),\n it: require('./it.json'),\n ja: require('./ja.json'),\n ja_pedantic: require('./ja_pedantic.json'),\n ko: require('./ko.json'),\n nb: require('./nb.json'),\n nl: require('./nl.json'),\n oc: require('./oc.json'),\n pl: require('./pl.json'),\n pt: require('./pt.json'),\n ro: require('./ro.json'),\n ru: require('./ru.json'),\n te: require('./te.json'),\n zh: require('./zh.json')\n}\n\nexport default messages\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-2a0da20a\\\",\\\"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","/* script */\nexport * from \"!!babel-loader!./video_attachment.js\"\nimport __vue_script__ from \"!!babel-loader!./video_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-6fce6a82\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./video_attachment.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","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","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","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!./scope_selector.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./scope_selector.js\"\nimport __vue_script__ from \"!!babel-loader!./scope_selector.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-28e8cbf1\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./scope_selector.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","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-input.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./emoji-input.js\"\nimport __vue_script__ from \"!!babel-loader!./emoji-input.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-5469beb4\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./emoji-input.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","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().startsWith(noPrefix))\n .sort((a, b) => {\n let aScore = 0\n let bScore = 0\n\n // Make custom emojis a priority\n aScore += a.imageUrl ? 10 : 0\n bScore += b.imageUrl ? 10 : 0\n\n // Sort alphabetically\n const alphabetically = a.displayText > b.displayText ? 1 : -1\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","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!./remote_follow.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./remote_follow.js\"\nimport __vue_script__ from \"!!babel-loader!./remote_follow.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-e95e446e\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./remote_follow.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","/* 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","const fetchUser = (attempt, user, store) => new Promise((resolve, reject) => {\n setTimeout(() => {\n store.state.api.backendInteractor.fetchUser({ id: user.id })\n .then((user) => store.commit('addNewUsers', [user]))\n .then(() => resolve([user.following, user.requested, user.locked, attempt]))\n .catch((e) => reject(e))\n }, 500)\n}).then(([following, sent, locked, attempt]) => {\n if (!following && !(locked && sent) && attempt <= 3) {\n // If we BE reports that we still not following that user - retry,\n // increment attempts by one\n return fetchUser(++attempt, user, store)\n } else {\n // If we run out of attempts, just return whatever status is.\n return sent\n }\n})\n\nexport const requestFollow = (user, store) => new Promise((resolve, reject) => {\n store.state.api.backendInteractor.followUser(user.id)\n .then((updated) => {\n store.commit('updateUserRelationship', [updated])\n\n if (updated.following || (user.locked && user.requested)) {\n // If we get result immediately or the account is locked, just stop.\n resolve({ sent: updated.requested })\n return\n }\n\n // But usually we don't get result immediately, so we ask server\n // for updated user profile to confirm if we are following them\n // Sometimes it takes several tries. Sometimes we end up not following\n // user anyway, probably because they locked themselves and we\n // don't know that yet.\n // Recursive Promise, it will call itself up to 3 times.\n\n return fetchUser(1, user, store)\n .then((sent) => {\n resolve({ sent })\n })\n })\n})\n\nexport const requestUnfollow = (user, store) => new Promise((resolve, reject) => {\n store.state.api.backendInteractor.unfollowUser(user.id)\n .then((updated) => {\n store.commit('updateUserRelationship', [updated])\n resolve({\n updated\n })\n })\n})\n","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 isFunction from 'lodash/isFunction'\n\nconst getComponentOptions = (Component) => (isFunction(Component)) ? Component.options : Component\n\nconst getComponentProps = (Component) => getComponentOptions(Component).props\n\nexport {\n getComponentOptions,\n getComponentProps\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!./style_switcher.scss\")\n}\n/* script */\nexport * from \"!!babel-loader!./style_switcher.js\"\nimport __vue_script__ from \"!!babel-loader!./style_switcher.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-ea88f12a\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./style_switcher.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","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!./color_input.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!../../../node_modules/vue-loader/lib/selector?type=script&index=0!./color_input.vue\"\nimport __vue_script__ from \"!!babel-loader!../../../node_modules/vue-loader/lib/selector?type=script&index=0!./color_input.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-a377bb38\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./color_input.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","/* script */\nexport * from \"!!babel-loader!../../../node_modules/vue-loader/lib/selector?type=script&index=0!./opacity_input.vue\"\nimport __vue_script__ from \"!!babel-loader!../../../node_modules/vue-loader/lib/selector?type=script&index=0!./opacity_input.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-87056ae2\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./opacity_input.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","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-02484cf4\\\",\\\"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","/* script */\nexport * from \"!!babel-loader!./confirm.js\"\nimport __vue_script__ from \"!!babel-loader!./confirm.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-20b6e7b3\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./confirm.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","import LoginForm from '../login_form/login_form.vue'\nimport MFARecoveryForm from '../mfa_form/recovery_form.vue'\nimport MFATOTPForm from '../mfa_form/totp_form.vue'\nimport { mapGetters } from 'vuex'\n\nconst AuthForm = {\n name: 'AuthForm',\n render (createElement) {\n return createElement('component', { is: this.authForm })\n },\n computed: {\n authForm () {\n if (this.requiredTOTP) { return 'MFATOTPForm' }\n if (this.requiredRecovery) { return 'MFARecoveryForm' }\n return 'LoginForm'\n },\n ...mapGetters('authFlow', ['requiredTOTP', 'requiredRecovery'])\n },\n components: {\n MFARecoveryForm,\n MFATOTPForm,\n LoginForm\n }\n}\n\nexport default AuthForm\n","const verifyOTPCode = ({ app, instance, mfaToken, code }) => {\n const url = `${instance}/oauth/mfa/challenge`\n const form = new window.FormData()\n\n form.append('client_id', app.client_id)\n form.append('client_secret', app.client_secret)\n form.append('mfa_token', mfaToken)\n form.append('code', code)\n form.append('challenge_type', 'totp')\n\n return window.fetch(url, {\n method: 'POST',\n body: form\n }).then((data) => data.json())\n}\n\nconst verifyRecoveryCode = ({ app, instance, mfaToken, code }) => {\n const url = `${instance}/oauth/mfa/challenge`\n const form = new window.FormData()\n\n form.append('client_id', app.client_id)\n form.append('client_secret', app.client_secret)\n form.append('mfa_token', mfaToken)\n form.append('code', code)\n form.append('challenge_type', 'recovery')\n\n return window.fetch(url, {\n method: 'POST',\n body: form\n }).then((data) => data.json())\n}\n\nconst mfa = {\n verifyOTPCode,\n verifyRecoveryCode\n}\n\nexport default mfa\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!./chat_panel.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./chat_panel.js\"\nimport __vue_script__ from \"!!babel-loader!./chat_panel.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-7ea51572\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./chat_panel.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","/* script */\nexport * from \"!!babel-loader!./instance_specific_panel.js\"\nimport __vue_script__ from \"!!babel-loader!./instance_specific_panel.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-5b01187b\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./instance_specific_panel.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","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!./features_panel.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./features_panel.js\"\nimport __vue_script__ from \"!!babel-loader!./features_panel.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-3443e05c\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./features_panel.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","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!./side_drawer.vue\")\n}\n/* script */\nexport * from \"!!babel-loader!./side_drawer.js\"\nimport __vue_script__ from \"!!babel-loader!./side_drawer.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-97c834ba\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./side_drawer.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","\nconst DIRECTION_LEFT = [-1, 0]\nconst DIRECTION_RIGHT = [1, 0]\nconst DIRECTION_UP = [0, -1]\nconst DIRECTION_DOWN = [0, 1]\n\nconst deltaCoord = (oldCoord, newCoord) => [newCoord[0] - oldCoord[0], newCoord[1] - oldCoord[1]]\n\nconst touchEventCoord = e => ([e.touches[0].screenX, e.touches[0].screenY])\n\nconst vectorLength = v => Math.sqrt(v[0] * v[0] + v[1] * v[1])\n\nconst perpendicular = v => [v[1], -v[0]]\n\nconst dotProduct = (v1, v2) => v1[0] * v2[0] + v1[1] * v2[1]\n\nconst project = (v1, v2) => {\n const scalar = (dotProduct(v1, v2) / dotProduct(v2, v2))\n return [scalar * v2[0], scalar * v2[1]]\n}\n\n// direction: either use the constants above or an arbitrary 2d vector.\n// threshold: how many Px to move from touch origin before checking if the\n// callback should be called.\n// divergentTolerance: a scalar for much of divergent direction we tolerate when\n// above threshold. for example, with 1.0 we only call the callback if\n// divergent component of delta is < 1.0 * direction component of delta.\nconst swipeGesture = (direction, onSwipe, threshold = 30, perpendicularTolerance = 1.0) => {\n return {\n direction,\n onSwipe,\n threshold,\n perpendicularTolerance,\n _startPos: [0, 0],\n _swiping: false\n }\n}\n\nconst beginSwipe = (event, gesture) => {\n gesture._startPos = touchEventCoord(event)\n gesture._swiping = true\n}\n\nconst updateSwipe = (event, gesture) => {\n if (!gesture._swiping) return\n // movement too small\n const delta = deltaCoord(gesture._startPos, touchEventCoord(event))\n if (vectorLength(delta) < gesture.threshold) return\n // movement is opposite from direction\n if (dotProduct(delta, gesture.direction) < 0) return\n // movement perpendicular to direction is too much\n const towardsDir = project(delta, gesture.direction)\n const perpendicularDir = perpendicular(gesture.direction)\n const towardsPerpendicular = project(delta, perpendicularDir)\n if (\n vectorLength(towardsDir) * gesture.perpendicularTolerance <\n vectorLength(towardsPerpendicular)\n ) return\n\n gesture.onSwipe()\n gesture._swiping = false\n}\n\nconst GestureService = {\n DIRECTION_LEFT,\n DIRECTION_RIGHT,\n DIRECTION_UP,\n DIRECTION_DOWN,\n swipeGesture,\n beginSwipe,\n updateSwipe\n}\n\nexport default GestureService\n","\nexport const windowWidth = () =>\n window.innerWidth ||\n document.documentElement.clientWidth ||\n document.body.clientWidth\n","import Vue from 'vue'\nimport VueRouter from 'vue-router'\nimport Vuex from 'vuex'\n\nimport interfaceModule from './modules/interface.js'\nimport instanceModule from './modules/instance.js'\nimport statusesModule from './modules/statuses.js'\nimport usersModule from './modules/users.js'\nimport apiModule from './modules/api.js'\nimport configModule from './modules/config.js'\nimport chatModule from './modules/chat.js'\nimport oauthModule from './modules/oauth.js'\nimport authFlowModule from './modules/auth_flow.js'\nimport mediaViewerModule from './modules/media_viewer.js'\nimport oauthTokensModule from './modules/oauth_tokens.js'\nimport reportsModule from './modules/reports.js'\nimport pollsModule from './modules/polls.js'\n\nimport VueI18n from 'vue-i18n'\n\nimport createPersistedState from './lib/persisted_state.js'\nimport pushNotifications from './lib/push_notifications_plugin.js'\n\nimport messages from './i18n/messages.js'\n\nimport VueChatScroll from 'vue-chat-scroll'\nimport VueClickOutside from 'v-click-outside'\nimport PortalVue from 'portal-vue'\nimport VTooltip from 'v-tooltip'\n\nimport afterStoreSetup from './boot/after_store.js'\n\nconst currentLocale = (window.navigator.language || 'en').split('-')[0]\n\nVue.use(Vuex)\nVue.use(VueRouter)\nVue.use(VueI18n)\nVue.use(VueChatScroll)\nVue.use(VueClickOutside)\nVue.use(PortalVue)\nVue.use(VTooltip)\n\nconst i18n = new VueI18n({\n // By default, use the browser locale, we will update it if neccessary\n locale: currentLocale,\n fallbackLocale: 'en',\n messages\n})\n\nconst persistedStateOptions = {\n paths: [\n 'config',\n 'users.lastLoginName',\n 'oauth'\n ]\n};\n\n(async () => {\n const persistedState = await createPersistedState(persistedStateOptions)\n const store = new Vuex.Store({\n modules: {\n i18n: {\n getters: {\n i18n: () => i18n\n }\n },\n interface: interfaceModule,\n instance: instanceModule,\n statuses: statusesModule,\n users: usersModule,\n api: apiModule,\n config: configModule,\n chat: chatModule,\n oauth: oauthModule,\n authFlow: authFlowModule,\n mediaViewer: mediaViewerModule,\n oauthTokens: oauthTokensModule,\n reports: reportsModule,\n polls: pollsModule\n },\n plugins: [persistedState, pushNotifications],\n strict: false // Socket modifies itself, let's ignore this for now.\n // strict: process.env.NODE_ENV !== 'production'\n })\n\n afterStoreSetup({ store, i18n })\n})()\n\n// These are inlined by webpack's DefinePlugin\n/* eslint-disable */\nwindow.___pleromafe_mode = process.env\nwindow.___pleromafe_commit_hash = COMMIT_HASH\nwindow.___pleromafe_dev_overrides = DEV_OVERRIDES\n","import { set, delete as del } from 'vue'\n\nconst defaultState = {\n settings: {\n currentSaveStateNotice: null,\n noticeClearTimeout: null,\n notificationPermission: null\n },\n browserSupport: {\n cssFilter: window.CSS && window.CSS.supports && (\n window.CSS.supports('filter', 'drop-shadow(0 0)') ||\n window.CSS.supports('-webkit-filter', 'drop-shadow(0 0)')\n )\n },\n mobileLayout: false\n}\n\nconst interfaceMod = {\n state: defaultState,\n mutations: {\n settingsSaved (state, { success, error }) {\n if (success) {\n if (state.noticeClearTimeout) {\n clearTimeout(state.noticeClearTimeout)\n }\n set(state.settings, 'currentSaveStateNotice', { error: false, data: success })\n set(state.settings, 'noticeClearTimeout',\n setTimeout(() => del(state.settings, 'currentSaveStateNotice'), 2000))\n } else {\n set(state.settings, 'currentSaveStateNotice', { error: true, errorData: error })\n }\n },\n setNotificationPermission (state, permission) {\n state.notificationPermission = permission\n },\n setMobileLayout (state, value) {\n state.mobileLayout = value\n }\n },\n actions: {\n setPageTitle ({ rootState }, option = '') {\n document.title = `${option} ${rootState.instance.name}`\n },\n settingsSaved ({ commit, dispatch }, { success, error }) {\n commit('settingsSaved', { success, error })\n },\n setNotificationPermission ({ commit }, permission) {\n commit('setNotificationPermission', permission)\n },\n setMobileLayout ({ commit }, value) {\n commit('setMobileLayout', value)\n }\n }\n}\n\nexport default interfaceMod\n","import { set } from 'vue'\nimport { setPreset } from '../services/style_setter/style_setter.js'\n\nconst defaultState = {\n // Stuff from static/config.json and apiConfig\n name: 'Pleroma FE',\n registrationOpen: true,\n safeDM: true,\n textlimit: 5000,\n server: 'http://localhost:4040/',\n theme: 'pleroma-dark',\n background: '/static/aurora_borealis.jpg',\n logo: '/static/logo.png',\n logoMask: true,\n logoMargin: '.2em',\n redirectRootNoLogin: '/main/all',\n redirectRootLogin: '/main/friends',\n showInstanceSpecificPanel: false,\n alwaysShowSubjectInput: true,\n hideMutedPosts: false,\n collapseMessageWithSubject: false,\n hidePostStats: false,\n hideUserStats: false,\n hideFilteredStatuses: false,\n disableChat: false,\n scopeCopy: true,\n subjectLineBehavior: 'email',\n postContentType: 'text/plain',\n nsfwCensorImage: undefined,\n vapidPublicKey: undefined,\n noAttachmentLinks: false,\n showFeaturesPanel: true,\n minimalScopesMode: false,\n\n // Nasty stuff\n pleromaBackend: true,\n emoji: [],\n customEmoji: [],\n restrictedNicknames: [],\n postFormats: [],\n\n // Feature-set, apparently, not everything here is reported...\n mediaProxyAvailable: false,\n chatAvailable: false,\n gopherAvailable: false,\n suggestionsEnabled: false,\n suggestionsWeb: '',\n\n // Html stuff\n instanceSpecificPanelContent: '',\n tos: '',\n\n // Version Information\n backendVersion: '',\n frontendVersion: '',\n\n pollsAvailable: false,\n pollLimits: {\n max_options: 4,\n max_option_chars: 255,\n min_expiration: 60,\n max_expiration: 60 * 60 * 24\n }\n}\n\nconst instance = {\n state: defaultState,\n mutations: {\n setInstanceOption (state, { name, value }) {\n if (typeof value !== 'undefined') {\n set(state, name, value)\n }\n }\n },\n actions: {\n setInstanceOption ({ commit, dispatch }, { name, value }) {\n commit('setInstanceOption', { name, value })\n switch (name) {\n case 'name':\n dispatch('setPageTitle')\n break\n case 'chatAvailable':\n if (value) {\n dispatch('initializeSocket')\n }\n break\n }\n },\n setTheme ({ commit }, themeName) {\n commit('setInstanceOption', { name: 'theme', value: themeName })\n return setPreset(themeName, commit)\n }\n }\n}\n\nexport default instance\n","import { remove, slice, each, findIndex, find, maxBy, minBy, merge, first, last, isArray, omitBy } from 'lodash'\nimport { set } from 'vue'\nimport apiService from '../services/api/api.service.js'\n// import parse from '../services/status_parser/status_parser.js'\n\nconst emptyTl = (userId = 0) => ({\n statuses: [],\n statusesObject: {},\n faves: [],\n visibleStatuses: [],\n visibleStatusesObject: {},\n newStatusCount: 0,\n maxId: 0,\n minId: 0,\n minVisibleId: 0,\n loading: false,\n followers: [],\n friends: [],\n userId,\n flushMarker: 0\n})\n\nconst emptyNotifications = () => ({\n desktopNotificationSilence: true,\n maxId: 0,\n minId: Number.POSITIVE_INFINITY,\n data: [],\n idStore: {},\n loading: false,\n error: false\n})\n\nexport const defaultState = () => ({\n allStatuses: [],\n allStatusesObject: {},\n conversationsObject: {},\n maxId: 0,\n notifications: emptyNotifications(),\n favorites: new Set(),\n error: false,\n timelines: {\n mentions: emptyTl(),\n public: emptyTl(),\n user: emptyTl(),\n favorites: emptyTl(),\n media: emptyTl(),\n publicAndExternal: emptyTl(),\n friends: emptyTl(),\n tag: emptyTl(),\n dms: emptyTl()\n }\n})\n\nexport const prepareStatus = (status) => {\n // Set deleted flag\n status.deleted = false\n\n // To make the array reactive\n status.attachments = status.attachments || []\n\n return status\n}\n\nconst visibleNotificationTypes = (rootState) => {\n return [\n rootState.config.notificationVisibility.likes && 'like',\n rootState.config.notificationVisibility.mentions && 'mention',\n rootState.config.notificationVisibility.repeats && 'repeat',\n rootState.config.notificationVisibility.follows && 'follow'\n ].filter(_ => _)\n}\n\nconst mergeOrAdd = (arr, obj, item) => {\n const oldItem = obj[item.id]\n\n if (oldItem) {\n // We already have this, so only merge the new info.\n // We ignore null values to avoid overwriting existing properties with missing data\n // we also skip 'user' because that is handled by users module\n merge(oldItem, omitBy(item, (v, k) => v === null || k === 'user'))\n // Reactivity fix.\n oldItem.attachments.splice(oldItem.attachments.length)\n return { item: oldItem, new: false }\n } else {\n // This is a new item, prepare it\n prepareStatus(item)\n arr.push(item)\n set(obj, item.id, item)\n return { item, new: true }\n }\n}\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\nconst sortTimeline = (timeline) => {\n timeline.visibleStatuses = timeline.visibleStatuses.sort(sortById)\n timeline.statuses = timeline.statuses.sort(sortById)\n timeline.minVisibleId = (last(timeline.visibleStatuses) || {}).id\n return timeline\n}\n\n// Add status to the global storages (arrays and objects maintaining statuses) except timelines\nconst addStatusToGlobalStorage = (state, data) => {\n const result = mergeOrAdd(state.allStatuses, state.allStatusesObject, data)\n if (result.new) {\n // Add to conversation\n const status = result.item\n const conversationsObject = state.conversationsObject\n const conversationId = status.statusnet_conversation_id\n if (conversationsObject[conversationId]) {\n conversationsObject[conversationId].push(status)\n } else {\n set(conversationsObject, conversationId, [status])\n }\n }\n return result\n}\n\n// Remove status from the global storages (arrays and objects maintaining statuses) except timelines\nconst removeStatusFromGlobalStorage = (state, status) => {\n remove(state.allStatuses, { id: status.id })\n\n // TODO: Need to remove from allStatusesObject?\n\n // Remove possible notification\n remove(state.notifications.data, ({ action: { id } }) => id === status.id)\n\n // Remove from conversation\n const conversationId = status.statusnet_conversation_id\n if (state.conversationsObject[conversationId]) {\n remove(state.conversationsObject[conversationId], { id: status.id })\n }\n}\n\nconst addNewStatuses = (state, { statuses, showImmediately = false, timeline, user = {},\n noIdUpdate = false, userId }) => {\n // Sanity check\n if (!isArray(statuses)) {\n return false\n }\n\n const allStatuses = state.allStatuses\n const timelineObject = state.timelines[timeline]\n\n const maxNew = statuses.length > 0 ? maxBy(statuses, 'id').id : 0\n const minNew = statuses.length > 0 ? minBy(statuses, 'id').id : 0\n const newer = timeline && (maxNew > timelineObject.maxId || timelineObject.maxId === 0) && statuses.length > 0\n const older = timeline && (minNew < timelineObject.minId || timelineObject.minId === 0) && statuses.length > 0\n\n if (!noIdUpdate && newer) {\n timelineObject.maxId = maxNew\n }\n if (!noIdUpdate && older) {\n timelineObject.minId = minNew\n }\n\n // This makes sure that user timeline won't get data meant for other\n // user. I.e. opening different user profiles makes request which could\n // return data late after user already viewing different user profile\n if ((timeline === 'user' || timeline === 'media') && timelineObject.userId !== userId) {\n return\n }\n\n const addStatus = (data, showImmediately, addToTimeline = true) => {\n const result = addStatusToGlobalStorage(state, data)\n const status = result.item\n\n if (result.new) {\n // We are mentioned in a post\n if (status.type === 'status' && find(status.attentions, { id: user.id })) {\n const mentions = state.timelines.mentions\n\n // Add the mention to the mentions timeline\n if (timelineObject !== mentions) {\n mergeOrAdd(mentions.statuses, mentions.statusesObject, status)\n mentions.newStatusCount += 1\n\n sortTimeline(mentions)\n }\n }\n if (status.visibility === 'direct') {\n const dms = state.timelines.dms\n\n mergeOrAdd(dms.statuses, dms.statusesObject, status)\n dms.newStatusCount += 1\n\n sortTimeline(dms)\n }\n }\n\n // Decide if we should treat the status as new for this timeline.\n let resultForCurrentTimeline\n // Some statuses should only be added to the global status repository.\n if (timeline && addToTimeline) {\n resultForCurrentTimeline = mergeOrAdd(timelineObject.statuses, timelineObject.statusesObject, status)\n }\n\n if (timeline && showImmediately) {\n // Add it directly to the visibleStatuses, don't change\n // newStatusCount\n mergeOrAdd(timelineObject.visibleStatuses, timelineObject.visibleStatusesObject, status)\n } else if (timeline && addToTimeline && resultForCurrentTimeline.new) {\n // Just change newStatuscount\n timelineObject.newStatusCount += 1\n }\n\n return status\n }\n\n const favoriteStatus = (favorite, counter) => {\n const status = find(allStatuses, { id: favorite.in_reply_to_status_id })\n if (status) {\n // This is our favorite, so the relevant bit.\n if (favorite.user.id === user.id) {\n status.favorited = true\n } else {\n status.fave_num += 1\n }\n }\n return status\n }\n\n const processors = {\n 'status': (status) => {\n addStatus(status, showImmediately)\n },\n 'retweet': (status) => {\n // RetweetedStatuses are never shown immediately\n const retweetedStatus = addStatus(status.retweeted_status, false, false)\n\n let retweet\n // If the retweeted status is already there, don't add the retweet\n // to the timeline.\n if (timeline && find(timelineObject.statuses, (s) => {\n if (s.retweeted_status) {\n return s.id === retweetedStatus.id || s.retweeted_status.id === retweetedStatus.id\n } else {\n return s.id === retweetedStatus.id\n }\n })) {\n // Already have it visible (either as the original or another RT), don't add to timeline, don't show.\n retweet = addStatus(status, false, false)\n } else {\n retweet = addStatus(status, showImmediately)\n }\n\n retweet.retweeted_status = retweetedStatus\n },\n 'favorite': (favorite) => {\n // Only update if this is a new favorite.\n // Ignore our own favorites because we get info about likes as response to like request\n if (!state.favorites.has(favorite.id)) {\n state.favorites.add(favorite.id)\n favoriteStatus(favorite)\n }\n },\n 'deletion': (deletion) => {\n const uri = deletion.uri\n const status = find(allStatuses, { uri })\n if (!status) {\n return\n }\n\n removeStatusFromGlobalStorage(state, status)\n\n if (timeline) {\n remove(timelineObject.statuses, { uri })\n remove(timelineObject.visibleStatuses, { uri })\n }\n },\n 'follow': (follow) => {\n // NOOP, it is known status but we don't do anything about it for now\n },\n 'default': (unknown) => {\n console.log('unknown status type')\n console.log(unknown)\n }\n }\n\n each(statuses, (status) => {\n const type = status.type\n const processor = processors[type] || processors['default']\n processor(status)\n })\n\n // Keep the visible statuses sorted\n if (timeline) {\n sortTimeline(timelineObject)\n }\n}\n\nconst addNewNotifications = (state, { dispatch, notifications, older, visibleNotificationTypes, rootGetters }) => {\n each(notifications, (notification) => {\n if (notification.type !== 'follow') {\n notification.action = addStatusToGlobalStorage(state, notification.action).item\n notification.status = notification.status && addStatusToGlobalStorage(state, notification.status).item\n }\n\n // Only add a new notification if we don't have one for the same action\n if (!state.notifications.idStore.hasOwnProperty(notification.id)) {\n state.notifications.maxId = notification.id > state.notifications.maxId\n ? notification.id\n : state.notifications.maxId\n state.notifications.minId = notification.id < state.notifications.minId\n ? notification.id\n : state.notifications.minId\n\n state.notifications.data.push(notification)\n state.notifications.idStore[notification.id] = notification\n\n if ('Notification' in window && window.Notification.permission === 'granted') {\n const notifObj = {}\n const status = notification.status\n const title = notification.from_profile.name\n notifObj.icon = notification.from_profile.profile_image_url\n let i18nString\n switch (notification.type) {\n case 'like':\n i18nString = 'favorited_you'\n break\n case 'repeat':\n i18nString = 'repeated_you'\n break\n case 'follow':\n i18nString = 'followed_you'\n break\n }\n\n if (i18nString) {\n notifObj.body = rootGetters.i18n.t('notifications.' + i18nString)\n } else {\n notifObj.body = notification.status.text\n }\n\n // Shows first attached non-nsfw image, if any. Should add configuration for this somehow...\n if (status && status.attachments && status.attachments.length > 0 && !status.nsfw &&\n status.attachments[0].mimetype.startsWith('image/')) {\n notifObj.image = status.attachments[0].url\n }\n\n if (!notification.seen && !state.notifications.desktopNotificationSilence && visibleNotificationTypes.includes(notification.type)) {\n let notification = new window.Notification(title, notifObj)\n // Chrome is known for not closing notifications automatically\n // according to MDN, anyway.\n setTimeout(notification.close.bind(notification), 5000)\n }\n }\n } else if (notification.seen) {\n state.notifications.idStore[notification.id].seen = true\n }\n })\n}\n\nconst removeStatus = (state, { timeline, userId }) => {\n const timelineObject = state.timelines[timeline]\n if (userId) {\n remove(timelineObject.statuses, { user: { id: userId } })\n remove(timelineObject.visibleStatuses, { user: { id: userId } })\n timelineObject.minVisibleId = timelineObject.visibleStatuses.length > 0 ? last(timelineObject.visibleStatuses).id : 0\n timelineObject.maxId = timelineObject.statuses.length > 0 ? first(timelineObject.statuses).id : 0\n }\n}\n\nexport const mutations = {\n addNewStatuses,\n addNewNotifications,\n removeStatus,\n showNewStatuses (state, { timeline }) {\n const oldTimeline = (state.timelines[timeline])\n\n oldTimeline.newStatusCount = 0\n oldTimeline.visibleStatuses = slice(oldTimeline.statuses, 0, 50)\n oldTimeline.minVisibleId = last(oldTimeline.visibleStatuses).id\n oldTimeline.minId = oldTimeline.minVisibleId\n oldTimeline.visibleStatusesObject = {}\n each(oldTimeline.visibleStatuses, (status) => { oldTimeline.visibleStatusesObject[status.id] = status })\n },\n resetStatuses (state) {\n const emptyState = defaultState()\n Object.entries(emptyState).forEach(([key, value]) => {\n state[key] = value\n })\n },\n clearTimeline (state, { timeline, excludeUserId = false }) {\n const userId = excludeUserId ? state.timelines[timeline].userId : undefined\n state.timelines[timeline] = emptyTl(userId)\n },\n clearNotifications (state) {\n state.notifications = emptyNotifications()\n },\n setFavorited (state, { status, value }) {\n const newStatus = state.allStatusesObject[status.id]\n\n if (newStatus.favorited !== value) {\n if (value) {\n newStatus.fave_num++\n } else {\n newStatus.fave_num--\n }\n }\n\n newStatus.favorited = value\n },\n setFavoritedConfirm (state, { status, user }) {\n const newStatus = state.allStatusesObject[status.id]\n newStatus.favorited = status.favorited\n newStatus.fave_num = status.fave_num\n const index = findIndex(newStatus.favoritedBy, { id: user.id })\n if (index !== -1 && !newStatus.favorited) {\n newStatus.favoritedBy.splice(index, 1)\n } else if (index === -1 && newStatus.favorited) {\n newStatus.favoritedBy.push(user)\n }\n },\n setMuted (state, status) {\n const newStatus = state.allStatusesObject[status.id]\n newStatus.muted = status.muted\n },\n setRetweeted (state, { status, value }) {\n const newStatus = state.allStatusesObject[status.id]\n\n if (newStatus.repeated !== value) {\n if (value) {\n newStatus.repeat_num++\n } else {\n newStatus.repeat_num--\n }\n }\n\n newStatus.repeated = value\n },\n setRetweetedConfirm (state, { status, user }) {\n const newStatus = state.allStatusesObject[status.id]\n newStatus.repeated = status.repeated\n newStatus.repeat_num = status.repeat_num\n const index = findIndex(newStatus.rebloggedBy, { id: user.id })\n if (index !== -1 && !newStatus.repeated) {\n newStatus.rebloggedBy.splice(index, 1)\n } else if (index === -1 && newStatus.repeated) {\n newStatus.rebloggedBy.push(user)\n }\n },\n setDeleted (state, { status }) {\n const newStatus = state.allStatusesObject[status.id]\n newStatus.deleted = true\n },\n setManyDeleted (state, condition) {\n Object.values(state.allStatusesObject).forEach(status => {\n if (condition(status)) {\n status.deleted = true\n }\n })\n },\n setLoading (state, { timeline, value }) {\n state.timelines[timeline].loading = value\n },\n setNsfw (state, { id, nsfw }) {\n const newStatus = state.allStatusesObject[id]\n newStatus.nsfw = nsfw\n },\n setError (state, { value }) {\n state.error = value\n },\n setNotificationsLoading (state, { value }) {\n state.notifications.loading = value\n },\n setNotificationsError (state, { value }) {\n state.notifications.error = value\n },\n setNotificationsSilence (state, { value }) {\n state.notifications.desktopNotificationSilence = value\n },\n markNotificationsAsSeen (state) {\n each(state.notifications.data, (notification) => {\n notification.seen = true\n })\n },\n queueFlush (state, { timeline, id }) {\n state.timelines[timeline].flushMarker = id\n },\n addRepeats (state, { id, rebloggedByUsers, currentUser }) {\n const newStatus = state.allStatusesObject[id]\n newStatus.rebloggedBy = rebloggedByUsers.filter(_ => _)\n // repeats stats can be incorrect based on polling condition, let's update them using the most recent data\n newStatus.repeat_num = newStatus.rebloggedBy.length\n newStatus.repeated = !!newStatus.rebloggedBy.find(({ id }) => currentUser.id === id)\n },\n addFavs (state, { id, favoritedByUsers, currentUser }) {\n const newStatus = state.allStatusesObject[id]\n newStatus.favoritedBy = favoritedByUsers.filter(_ => _)\n // favorites stats can be incorrect based on polling condition, let's update them using the most recent data\n newStatus.fave_num = newStatus.favoritedBy.length\n newStatus.favorited = !!newStatus.favoritedBy.find(({ id }) => currentUser.id === id)\n },\n updateStatusWithPoll (state, { id, poll }) {\n const status = state.allStatusesObject[id]\n status.poll = poll\n }\n}\n\nconst statuses = {\n state: defaultState(),\n actions: {\n addNewStatuses ({ rootState, commit }, { statuses, showImmediately = false, timeline = false, noIdUpdate = false, userId }) {\n commit('addNewStatuses', { statuses, showImmediately, timeline, noIdUpdate, user: rootState.users.currentUser, userId })\n },\n addNewNotifications ({ rootState, commit, dispatch, rootGetters }, { notifications, older }) {\n commit('addNewNotifications', { visibleNotificationTypes: visibleNotificationTypes(rootState), dispatch, notifications, older, rootGetters })\n },\n setError ({ rootState, commit }, { value }) {\n commit('setError', { value })\n },\n setNotificationsLoading ({ rootState, commit }, { value }) {\n commit('setNotificationsLoading', { value })\n },\n setNotificationsError ({ rootState, commit }, { value }) {\n commit('setNotificationsError', { value })\n },\n setNotificationsSilence ({ rootState, commit }, { value }) {\n commit('setNotificationsSilence', { value })\n },\n deleteStatus ({ rootState, commit }, status) {\n commit('setDeleted', { status })\n apiService.deleteStatus({ id: status.id, credentials: rootState.users.currentUser.credentials })\n },\n markStatusesAsDeleted ({ commit }, condition) {\n commit('setManyDeleted', condition)\n },\n favorite ({ rootState, commit }, status) {\n // Optimistic favoriting...\n commit('setFavorited', { status, value: true })\n rootState.api.backendInteractor.favorite(status.id)\n .then(status => commit('setFavoritedConfirm', { status, user: rootState.users.currentUser }))\n },\n unfavorite ({ rootState, commit }, status) {\n // Optimistic unfavoriting...\n commit('setFavorited', { status, value: false })\n rootState.api.backendInteractor.unfavorite(status.id)\n .then(status => commit('setFavoritedConfirm', { status, user: rootState.users.currentUser }))\n },\n fetchPinnedStatuses ({ rootState, dispatch }, userId) {\n rootState.api.backendInteractor.fetchPinnedStatuses(userId)\n .then(statuses => dispatch('addNewStatuses', { statuses, timeline: 'user', userId, showImmediately: true, noIdUpdate: true }))\n },\n pinStatus ({ rootState, dispatch }, statusId) {\n return rootState.api.backendInteractor.pinOwnStatus(statusId)\n .then((status) => dispatch('addNewStatuses', { statuses: [status] }))\n },\n unpinStatus ({ rootState, dispatch }, statusId) {\n rootState.api.backendInteractor.unpinOwnStatus(statusId)\n .then((status) => dispatch('addNewStatuses', { statuses: [status] }))\n },\n muteConversation ({ rootState, commit }, statusId) {\n return rootState.api.backendInteractor.muteConversation(statusId)\n .then((status) => commit('setMuted', status))\n },\n unmuteConversation ({ rootState, commit }, statusId) {\n return rootState.api.backendInteractor.unmuteConversation(statusId)\n .then((status) => commit('setMuted', status))\n },\n retweet ({ rootState, commit }, status) {\n // Optimistic retweeting...\n commit('setRetweeted', { status, value: true })\n rootState.api.backendInteractor.retweet(status.id)\n .then(status => commit('setRetweetedConfirm', { status: status.retweeted_status, user: rootState.users.currentUser }))\n },\n unretweet ({ rootState, commit }, status) {\n // Optimistic unretweeting...\n commit('setRetweeted', { status, value: false })\n rootState.api.backendInteractor.unretweet(status.id)\n .then(status => commit('setRetweetedConfirm', { status, user: rootState.users.currentUser }))\n },\n queueFlush ({ rootState, commit }, { timeline, id }) {\n commit('queueFlush', { timeline, id })\n },\n markNotificationsAsSeen ({ rootState, commit }) {\n commit('markNotificationsAsSeen')\n apiService.markNotificationsAsSeen({\n id: rootState.statuses.notifications.maxId,\n credentials: rootState.users.currentUser.credentials\n })\n },\n fetchFavsAndRepeats ({ rootState, commit }, id) {\n Promise.all([\n rootState.api.backendInteractor.fetchFavoritedByUsers(id),\n rootState.api.backendInteractor.fetchRebloggedByUsers(id)\n ]).then(([favoritedByUsers, rebloggedByUsers]) => {\n commit('addFavs', { id, favoritedByUsers, currentUser: rootState.users.currentUser })\n commit('addRepeats', { id, rebloggedByUsers, currentUser: rootState.users.currentUser })\n })\n },\n fetchFavs ({ rootState, commit }, id) {\n rootState.api.backendInteractor.fetchFavoritedByUsers(id)\n .then(favoritedByUsers => commit('addFavs', { id, favoritedByUsers, currentUser: rootState.users.currentUser }))\n },\n fetchRepeats ({ rootState, commit }, id) {\n rootState.api.backendInteractor.fetchRebloggedByUsers(id)\n .then(rebloggedByUsers => commit('addRepeats', { id, rebloggedByUsers, currentUser: rootState.users.currentUser }))\n },\n search (store, { q, resolve, limit, offset, following }) {\n return store.rootState.api.backendInteractor.search2({ q, resolve, limit, offset, following })\n .then((data) => {\n store.commit('addNewUsers', data.accounts)\n store.commit('addNewStatuses', { statuses: data.statuses })\n return data\n })\n }\n },\n mutations\n}\n\nexport default statuses\n","const qvitterStatusType = (status) => {\n if (status.is_post_verb) {\n return 'status'\n }\n\n if (status.retweeted_status) {\n return 'retweet'\n }\n\n if ((typeof status.uri === 'string' && status.uri.match(/(fave|objectType=Favourite)/)) ||\n (typeof status.text === 'string' && status.text.match(/favorited/))) {\n return 'favorite'\n }\n\n if (status.text.match(/deleted notice {{tag/) || status.qvitter_delete_notice) {\n return 'deletion'\n }\n\n if (status.text.match(/started following/) || status.activity_type === 'follow') {\n return 'follow'\n }\n\n return 'unknown'\n}\n\nexport const parseUser = (data) => {\n const output = {}\n const masto = data.hasOwnProperty('acct')\n // case for users in \"mentions\" property for statuses in MastoAPI\n const mastoShort = masto && !data.hasOwnProperty('avatar')\n\n output.id = String(data.id)\n\n if (masto) {\n output.screen_name = data.acct\n output.statusnet_profile_url = data.url\n\n // There's nothing else to get\n if (mastoShort) {\n return output\n }\n\n output.name = data.display_name\n output.name_html = addEmojis(data.display_name, data.emojis)\n\n output.description = data.note\n output.description_html = addEmojis(data.note, data.emojis)\n\n // Utilize avatar_static for gif avatars?\n output.profile_image_url = data.avatar\n output.profile_image_url_original = data.avatar\n\n // Same, utilize header_static?\n output.cover_photo = data.header\n\n output.friends_count = data.following_count\n\n output.bot = data.bot\n\n if (data.pleroma) {\n const relationship = data.pleroma.relationship\n\n output.background_image = data.pleroma.background_image\n output.token = data.pleroma.chat_token\n\n if (relationship) {\n output.follows_you = relationship.followed_by\n output.requested = relationship.requested\n output.following = relationship.following\n output.statusnet_blocking = relationship.blocking\n output.muted = relationship.muting\n output.subscribed = relationship.subscribing\n }\n\n output.hide_follows = data.pleroma.hide_follows\n output.hide_followers = data.pleroma.hide_followers\n\n output.rights = {\n moderator: data.pleroma.is_moderator,\n admin: data.pleroma.is_admin\n }\n // TODO: Clean up in UI? This is duplication from what BE does for qvitterapi\n if (output.rights.admin) {\n output.role = 'admin'\n } else if (output.rights.moderator) {\n output.role = 'moderator'\n } else {\n output.role = 'member'\n }\n }\n\n if (data.source) {\n output.description = data.source.note\n output.default_scope = data.source.privacy\n if (data.source.pleroma) {\n output.no_rich_text = data.source.pleroma.no_rich_text\n output.show_role = data.source.pleroma.show_role\n }\n }\n\n // TODO: handle is_local\n output.is_local = !output.screen_name.includes('@')\n } else {\n output.screen_name = data.screen_name\n\n output.name = data.name\n output.name_html = data.name_html\n\n output.description = data.description\n output.description_html = data.description_html\n\n output.profile_image_url = data.profile_image_url\n output.profile_image_url_original = data.profile_image_url_original\n\n output.cover_photo = data.cover_photo\n\n output.friends_count = data.friends_count\n\n // output.bot = ??? missing\n\n output.statusnet_profile_url = data.statusnet_profile_url\n\n output.statusnet_blocking = data.statusnet_blocking\n\n output.is_local = data.is_local\n output.role = data.role\n output.show_role = data.show_role\n\n output.follows_you = data.follows_you\n\n output.muted = data.muted\n\n if (data.rights) {\n output.rights = {\n moderator: data.rights.delete_others_notice,\n admin: data.rights.admin\n }\n }\n output.no_rich_text = data.no_rich_text\n output.default_scope = data.default_scope\n output.hide_follows = data.hide_follows\n output.hide_followers = data.hide_followers\n output.background_image = data.background_image\n // on mastoapi this info is contained in a \"relationship\"\n output.following = data.following\n // Websocket token\n output.token = data.token\n }\n\n output.created_at = new Date(data.created_at)\n output.locked = data.locked\n output.followers_count = data.followers_count\n output.statuses_count = data.statuses_count\n output.friendIds = []\n output.followerIds = []\n output.pinnedStatusIds = []\n\n if (data.pleroma) {\n output.follow_request_count = data.pleroma.follow_request_count\n\n output.tags = data.pleroma.tags\n output.deactivated = data.pleroma.deactivated\n\n output.notification_settings = data.pleroma.notification_settings\n }\n\n output.tags = output.tags || []\n output.rights = output.rights || {}\n output.notification_settings = output.notification_settings || {}\n\n return output\n}\n\nexport const parseAttachment = (data) => {\n const output = {}\n const masto = !data.hasOwnProperty('oembed')\n\n if (masto) {\n // Not exactly same...\n output.mimetype = data.pleroma ? data.pleroma.mime_type : data.type\n output.meta = data.meta // not present in BE yet\n output.id = data.id\n } else {\n output.mimetype = data.mimetype\n // output.meta = ??? missing\n }\n\n output.url = data.url\n output.description = data.description\n\n return output\n}\nexport const addEmojis = (string, emojis) => {\n return emojis.reduce((acc, emoji) => {\n return acc.replace(\n new RegExp(`:${emoji.shortcode}:`, 'g'),\n `${emoji.shortcode}`\n )\n }, string)\n}\n\nexport const parseStatus = (data) => {\n const output = {}\n const masto = data.hasOwnProperty('account')\n\n if (masto) {\n output.favorited = data.favourited\n output.fave_num = data.favourites_count\n\n output.repeated = data.reblogged\n output.repeat_num = data.reblogs_count\n\n output.type = data.reblog ? 'retweet' : 'status'\n output.nsfw = data.sensitive\n\n output.statusnet_html = addEmojis(data.content, data.emojis)\n\n output.tags = data.tags\n\n if (data.pleroma) {\n const { pleroma } = data\n output.text = pleroma.content ? data.pleroma.content['text/plain'] : data.content\n output.summary = pleroma.spoiler_text ? data.pleroma.spoiler_text['text/plain'] : data.spoiler_text\n output.statusnet_conversation_id = data.pleroma.conversation_id\n output.is_local = pleroma.local\n output.in_reply_to_screen_name = data.pleroma.in_reply_to_account_acct\n } else {\n output.text = data.content\n output.summary = data.spoiler_text\n }\n\n output.in_reply_to_status_id = data.in_reply_to_id\n output.in_reply_to_user_id = data.in_reply_to_account_id\n output.replies_count = data.replies_count\n\n if (output.type === 'retweet') {\n output.retweeted_status = parseStatus(data.reblog)\n }\n\n output.summary_html = addEmojis(data.spoiler_text, data.emojis)\n output.external_url = data.url\n output.poll = data.poll\n output.pinned = data.pinned\n output.muted = data.muted\n } else {\n output.favorited = data.favorited\n output.fave_num = data.fave_num\n\n output.repeated = data.repeated\n output.repeat_num = data.repeat_num\n\n // catchall, temporary\n // Object.assign(output, data)\n\n output.type = qvitterStatusType(data)\n\n if (data.nsfw === undefined) {\n output.nsfw = isNsfw(data)\n if (data.retweeted_status) {\n output.nsfw = data.retweeted_status.nsfw\n }\n } else {\n output.nsfw = data.nsfw\n }\n\n output.statusnet_html = data.statusnet_html\n output.text = data.text\n\n output.in_reply_to_status_id = data.in_reply_to_status_id\n output.in_reply_to_user_id = data.in_reply_to_user_id\n output.in_reply_to_screen_name = data.in_reply_to_screen_name\n output.statusnet_conversation_id = data.statusnet_conversation_id\n\n if (output.type === 'retweet') {\n output.retweeted_status = parseStatus(data.retweeted_status)\n }\n\n output.summary = data.summary\n output.summary_html = data.summary_html\n output.external_url = data.external_url\n output.is_local = data.is_local\n }\n\n output.id = String(data.id)\n output.visibility = data.visibility\n output.card = data.card\n output.created_at = new Date(data.created_at)\n\n // Converting to string, the right way.\n output.in_reply_to_status_id = output.in_reply_to_status_id\n ? String(output.in_reply_to_status_id)\n : null\n output.in_reply_to_user_id = output.in_reply_to_user_id\n ? String(output.in_reply_to_user_id)\n : null\n\n output.user = parseUser(masto ? data.account : data.user)\n\n output.attentions = ((masto ? data.mentions : data.attentions) || []).map(parseUser)\n\n output.attachments = ((masto ? data.media_attachments : data.attachments) || [])\n .map(parseAttachment)\n\n const retweetedStatus = masto ? data.reblog : data.retweeted_status\n if (retweetedStatus) {\n output.retweeted_status = parseStatus(retweetedStatus)\n }\n\n output.favoritedBy = []\n output.rebloggedBy = []\n\n return output\n}\n\nexport const parseNotification = (data) => {\n const mastoDict = {\n 'favourite': 'like',\n 'reblog': 'repeat'\n }\n const masto = !data.hasOwnProperty('ntype')\n const output = {}\n\n if (masto) {\n output.type = mastoDict[data.type] || data.type\n output.seen = data.pleroma.is_seen\n output.status = output.type === 'follow'\n ? null\n : parseStatus(data.status)\n output.action = output.status // TODO: Refactor, this is unneeded\n output.from_profile = parseUser(data.account)\n } else {\n const parsedNotice = parseStatus(data.notice)\n output.type = data.ntype\n output.seen = Boolean(data.is_seen)\n output.status = output.type === 'like'\n ? parseStatus(data.notice.favorited_status)\n : parsedNotice\n output.action = parsedNotice\n output.from_profile = parseUser(data.from_profile)\n }\n\n output.created_at = new Date(data.created_at)\n output.id = parseInt(data.id)\n\n return output\n}\n\nconst isNsfw = (status) => {\n const nsfwRegex = /#nsfw/i\n return (status.tags || []).includes('nsfw') || !!(status.text || '').match(nsfwRegex)\n}\n","import { humanizeErrors } from '../../modules/errors'\n\nexport function StatusCodeError (statusCode, body, options, response) {\n this.name = 'StatusCodeError'\n this.statusCode = statusCode\n this.message = statusCode + ' - ' + (JSON && JSON.stringify ? JSON.stringify(body) : body)\n this.error = body // legacy attribute\n this.options = options\n this.response = response\n\n if (Error.captureStackTrace) { // required for non-V8 environments\n Error.captureStackTrace(this)\n }\n}\nStatusCodeError.prototype = Object.create(Error.prototype)\nStatusCodeError.prototype.constructor = StatusCodeError\n\nexport class RegistrationError extends Error {\n constructor (error) {\n super()\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this)\n }\n\n try {\n // the error is probably a JSON object with a single key, \"errors\", whose value is another JSON object containing the real errors\n if (typeof error === 'string') {\n error = JSON.parse(error)\n if (error.hasOwnProperty('error')) {\n error = JSON.parse(error.error)\n }\n }\n\n if (typeof error === 'object') {\n // replace ap_id with username\n if (error.ap_id) {\n error.username = error.ap_id\n delete error.ap_id\n }\n this.message = humanizeErrors(error)\n } else {\n this.message = error\n }\n } catch (e) {\n // can't parse it, so just treat it like a string\n this.message = error\n }\n }\n}\n","import { capitalize } from 'lodash'\n\nexport function humanizeErrors (errors) {\n return Object.entries(errors).reduce((errs, [k, val]) => {\n let message = val.reduce((acc, message) => {\n let key = capitalize(k.replace(/_/g, ' '))\n return acc + [key, message].join(' ') + '. '\n }, '')\n return [...errs, message]\n }, [])\n}\n","import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js'\nimport oauthApi from '../services/new_api/oauth.js'\nimport { compact, map, each, merge, last, concat, uniq } from 'lodash'\nimport { set } from 'vue'\nimport { registerPushNotifications, unregisterPushNotifications } from '../services/push/push.js'\n\n// TODO: Unify with mergeOrAdd in statuses.js\nexport const mergeOrAdd = (arr, obj, item) => {\n if (!item) { return false }\n const oldItem = obj[item.id]\n if (oldItem) {\n // We already have this, so only merge the new info.\n merge(oldItem, item)\n return { item: oldItem, new: false }\n } else {\n // This is a new item, prepare it\n arr.push(item)\n set(obj, item.id, item)\n if (item.screen_name && !item.screen_name.includes('@')) {\n set(obj, item.screen_name.toLowerCase(), item)\n }\n return { item, new: true }\n }\n}\n\nconst getNotificationPermission = () => {\n const Notification = window.Notification\n\n if (!Notification) return Promise.resolve(null)\n if (Notification.permission === 'default') return Notification.requestPermission()\n return Promise.resolve(Notification.permission)\n}\n\nconst blockUser = (store, id) => {\n return store.rootState.api.backendInteractor.blockUser(id)\n .then((relationship) => {\n store.commit('updateUserRelationship', [relationship])\n store.commit('addBlockId', id)\n store.commit('removeStatus', { timeline: 'friends', userId: id })\n store.commit('removeStatus', { timeline: 'public', userId: id })\n store.commit('removeStatus', { timeline: 'publicAndExternal', userId: id })\n })\n}\n\nconst unblockUser = (store, id) => {\n return store.rootState.api.backendInteractor.unblockUser(id)\n .then((relationship) => store.commit('updateUserRelationship', [relationship]))\n}\n\nconst muteUser = (store, id) => {\n return store.rootState.api.backendInteractor.muteUser(id)\n .then((relationship) => {\n store.commit('updateUserRelationship', [relationship])\n store.commit('addMuteId', id)\n })\n}\n\nconst unmuteUser = (store, id) => {\n return store.rootState.api.backendInteractor.unmuteUser(id)\n .then((relationship) => store.commit('updateUserRelationship', [relationship]))\n}\n\nexport const mutations = {\n setMuted (state, { user: { id }, muted }) {\n const user = state.usersObject[id]\n set(user, 'muted', muted)\n },\n tagUser (state, { user: { id }, tag }) {\n const user = state.usersObject[id]\n const tags = user.tags || []\n const newTags = tags.concat([tag])\n set(user, 'tags', newTags)\n },\n untagUser (state, { user: { id }, tag }) {\n const user = state.usersObject[id]\n const tags = user.tags || []\n const newTags = tags.filter(t => t !== tag)\n set(user, 'tags', newTags)\n },\n updateRight (state, { user: { id }, right, value }) {\n const user = state.usersObject[id]\n let newRights = user.rights\n newRights[right] = value\n set(user, 'rights', newRights)\n },\n updateActivationStatus (state, { user: { id }, status }) {\n const user = state.usersObject[id]\n set(user, 'deactivated', !status)\n },\n setCurrentUser (state, user) {\n state.lastLoginName = user.screen_name\n state.currentUser = merge(state.currentUser || {}, user)\n },\n clearCurrentUser (state) {\n state.currentUser = false\n state.lastLoginName = false\n },\n beginLogin (state) {\n state.loggingIn = true\n },\n endLogin (state) {\n state.loggingIn = false\n },\n saveFriendIds (state, { id, friendIds }) {\n const user = state.usersObject[id]\n user.friendIds = uniq(concat(user.friendIds, friendIds))\n },\n saveFollowerIds (state, { id, followerIds }) {\n const user = state.usersObject[id]\n user.followerIds = uniq(concat(user.followerIds, followerIds))\n },\n // Because frontend doesn't have a reason to keep these stuff in memory\n // outside of viewing someones user profile.\n clearFriends (state, userId) {\n const user = state.usersObject[userId]\n if (user) {\n set(user, 'friendIds', [])\n }\n },\n clearFollowers (state, userId) {\n const user = state.usersObject[userId]\n if (user) {\n set(user, 'followerIds', [])\n }\n },\n addNewUsers (state, users) {\n each(users, (user) => mergeOrAdd(state.users, state.usersObject, user))\n },\n updateUserRelationship (state, relationships) {\n relationships.forEach((relationship) => {\n const user = state.usersObject[relationship.id]\n if (user) {\n user.follows_you = relationship.followed_by\n user.following = relationship.following\n user.muted = relationship.muting\n user.statusnet_blocking = relationship.blocking\n user.subscribed = relationship.subscribing\n }\n })\n },\n updateBlocks (state, blockedUsers) {\n // Reset statusnet_blocking of all fetched users\n each(state.users, (user) => { user.statusnet_blocking = false })\n each(blockedUsers, (user) => mergeOrAdd(state.users, state.usersObject, user))\n },\n saveBlockIds (state, blockIds) {\n state.currentUser.blockIds = blockIds\n },\n addBlockId (state, blockId) {\n if (state.currentUser.blockIds.indexOf(blockId) === -1) {\n state.currentUser.blockIds.push(blockId)\n }\n },\n updateMutes (state, mutedUsers) {\n // Reset muted of all fetched users\n each(state.users, (user) => { user.muted = false })\n each(mutedUsers, (user) => mergeOrAdd(state.users, state.usersObject, user))\n },\n saveMuteIds (state, muteIds) {\n state.currentUser.muteIds = muteIds\n },\n addMuteId (state, muteId) {\n if (state.currentUser.muteIds.indexOf(muteId) === -1) {\n state.currentUser.muteIds.push(muteId)\n }\n },\n setPinnedToUser (state, status) {\n const user = state.usersObject[status.user.id]\n const index = user.pinnedStatusIds.indexOf(status.id)\n if (status.pinned && index === -1) {\n user.pinnedStatusIds.push(status.id)\n } else if (!status.pinned && index !== -1) {\n user.pinnedStatusIds.splice(index, 1)\n }\n },\n setUserForStatus (state, status) {\n status.user = state.usersObject[status.user.id]\n },\n setUserForNotification (state, notification) {\n if (notification.type !== 'follow') {\n notification.action.user = state.usersObject[notification.action.user.id]\n }\n notification.from_profile = state.usersObject[notification.from_profile.id]\n },\n setColor (state, { user: { id }, highlighted }) {\n const user = state.usersObject[id]\n set(user, 'highlight', highlighted)\n },\n signUpPending (state) {\n state.signUpPending = true\n state.signUpErrors = []\n },\n signUpSuccess (state) {\n state.signUpPending = false\n },\n signUpFailure (state, errors) {\n state.signUpPending = false\n state.signUpErrors = errors\n }\n}\n\nexport const getters = {\n findUser: state => query => {\n const result = state.usersObject[query]\n // In case it's a screen_name, we can try searching case-insensitive\n if (!result && typeof query === 'string') {\n return state.usersObject[query.toLowerCase()]\n }\n return result\n }\n}\n\nexport const defaultState = {\n loggingIn: false,\n lastLoginName: false,\n currentUser: false,\n users: [],\n usersObject: {},\n signUpPending: false,\n signUpErrors: []\n}\n\nconst users = {\n state: defaultState,\n mutations,\n getters,\n actions: {\n fetchUser (store, id) {\n return store.rootState.api.backendInteractor.fetchUser({ id })\n .then((user) => {\n store.commit('addNewUsers', [user])\n return user\n })\n },\n fetchUserRelationship (store, id) {\n if (store.state.currentUser) {\n store.rootState.api.backendInteractor.fetchUserRelationship({ id })\n .then((relationships) => store.commit('updateUserRelationship', relationships))\n }\n },\n fetchBlocks (store) {\n return store.rootState.api.backendInteractor.fetchBlocks()\n .then((blocks) => {\n store.commit('saveBlockIds', map(blocks, 'id'))\n store.commit('updateBlocks', blocks)\n return blocks\n })\n },\n blockUser (store, id) {\n return blockUser(store, id)\n },\n unblockUser (store, id) {\n return unblockUser(store, id)\n },\n blockUsers (store, ids = []) {\n return Promise.all(ids.map(id => blockUser(store, id)))\n },\n unblockUsers (store, ids = []) {\n return Promise.all(ids.map(id => unblockUser(store, id)))\n },\n fetchMutes (store) {\n return store.rootState.api.backendInteractor.fetchMutes()\n .then((mutes) => {\n store.commit('updateMutes', mutes)\n store.commit('saveMuteIds', map(mutes, 'id'))\n return mutes\n })\n },\n muteUser (store, id) {\n return muteUser(store, id)\n },\n unmuteUser (store, id) {\n return unmuteUser(store, id)\n },\n muteUsers (store, ids = []) {\n return Promise.all(ids.map(id => muteUser(store, id)))\n },\n unmuteUsers (store, ids = []) {\n return Promise.all(ids.map(id => unmuteUser(store, id)))\n },\n fetchFriends ({ rootState, commit }, id) {\n const user = rootState.users.usersObject[id]\n const maxId = last(user.friendIds)\n return rootState.api.backendInteractor.fetchFriends({ id, maxId })\n .then((friends) => {\n commit('addNewUsers', friends)\n commit('saveFriendIds', { id, friendIds: map(friends, 'id') })\n return friends\n })\n },\n fetchFollowers ({ rootState, commit }, id) {\n const user = rootState.users.usersObject[id]\n const maxId = last(user.followerIds)\n return rootState.api.backendInteractor.fetchFollowers({ id, maxId })\n .then((followers) => {\n commit('addNewUsers', followers)\n commit('saveFollowerIds', { id, followerIds: map(followers, 'id') })\n return followers\n })\n },\n clearFriends ({ commit }, userId) {\n commit('clearFriends', userId)\n },\n clearFollowers ({ commit }, userId) {\n commit('clearFollowers', userId)\n },\n subscribeUser ({ rootState, commit }, id) {\n return rootState.api.backendInteractor.subscribeUser(id)\n .then((relationship) => commit('updateUserRelationship', [relationship]))\n },\n unsubscribeUser ({ rootState, commit }, id) {\n return rootState.api.backendInteractor.unsubscribeUser(id)\n .then((relationship) => commit('updateUserRelationship', [relationship]))\n },\n registerPushNotifications (store) {\n const token = store.state.currentUser.credentials\n const vapidPublicKey = store.rootState.instance.vapidPublicKey\n const isEnabled = store.rootState.config.webPushNotifications\n const notificationVisibility = store.rootState.config.notificationVisibility\n\n registerPushNotifications(isEnabled, vapidPublicKey, token, notificationVisibility)\n },\n unregisterPushNotifications (store) {\n const token = store.state.currentUser.credentials\n\n unregisterPushNotifications(token)\n },\n addNewUsers ({ commit }, users) {\n commit('addNewUsers', users)\n },\n addNewStatuses (store, { statuses }) {\n const users = map(statuses, 'user')\n const retweetedUsers = compact(map(statuses, 'retweeted_status.user'))\n store.commit('addNewUsers', users)\n store.commit('addNewUsers', retweetedUsers)\n\n each(statuses, (status) => {\n // Reconnect users to statuses\n store.commit('setUserForStatus', status)\n // Set pinned statuses to user\n store.commit('setPinnedToUser', status)\n })\n each(compact(map(statuses, 'retweeted_status')), (status) => {\n // Reconnect users to retweets\n store.commit('setUserForStatus', status)\n // Set pinned retweets to user\n store.commit('setPinnedToUser', status)\n })\n },\n addNewNotifications (store, { notifications }) {\n const users = map(notifications, 'from_profile')\n const notificationIds = notifications.map(_ => _.id)\n store.commit('addNewUsers', users)\n\n const notificationsObject = store.rootState.statuses.notifications.idStore\n const relevantNotifications = Object.entries(notificationsObject)\n .filter(([k, val]) => notificationIds.includes(k))\n .map(([k, val]) => val)\n\n // Reconnect users to notifications\n each(relevantNotifications, (notification) => {\n store.commit('setUserForNotification', notification)\n })\n },\n searchUsers (store, query) {\n return store.rootState.api.backendInteractor.searchUsers(query)\n .then((users) => {\n store.commit('addNewUsers', users)\n return users\n })\n },\n async signUp (store, userInfo) {\n store.commit('signUpPending')\n\n let rootState = store.rootState\n\n try {\n let data = await rootState.api.backendInteractor.register(userInfo)\n store.commit('signUpSuccess')\n store.commit('setToken', data.access_token)\n store.dispatch('loginUser', data.access_token)\n } catch (e) {\n let errors = e.message\n store.commit('signUpFailure', errors)\n throw e\n }\n },\n async getCaptcha (store) {\n return store.rootState.api.backendInteractor.getCaptcha()\n },\n\n logout (store) {\n const { oauth, instance } = store.rootState\n\n const data = {\n ...oauth,\n commit: store.commit,\n instance: instance.server\n }\n\n return oauthApi.getOrCreateApp(data)\n .then((app) => {\n const params = {\n app,\n instance: data.instance,\n token: oauth.userToken\n }\n\n return oauthApi.revokeToken(params)\n })\n .then(() => {\n store.commit('clearCurrentUser')\n store.dispatch('disconnectFromSocket')\n store.commit('clearToken')\n store.dispatch('stopFetching', 'friends')\n store.commit('setBackendInteractor', backendInteractorService(store.getters.getToken()))\n store.dispatch('stopFetching', 'notifications')\n store.commit('clearNotifications')\n store.commit('resetStatuses')\n })\n },\n loginUser (store, accessToken) {\n return new Promise((resolve, reject) => {\n const commit = store.commit\n commit('beginLogin')\n store.rootState.api.backendInteractor.verifyCredentials(accessToken)\n .then((data) => {\n if (!data.error) {\n const user = data\n // user.credentials = userCredentials\n user.credentials = accessToken\n user.blockIds = []\n user.muteIds = []\n commit('setCurrentUser', user)\n commit('addNewUsers', [user])\n\n getNotificationPermission()\n .then(permission => commit('setNotificationPermission', permission))\n\n // Set our new backend interactor\n commit('setBackendInteractor', backendInteractorService(accessToken))\n\n if (user.token) {\n store.dispatch('setWsToken', user.token)\n\n // Initialize the chat socket.\n store.dispatch('initializeSocket')\n }\n\n // Start getting fresh posts.\n store.dispatch('startFetchingTimeline', { timeline: 'friends' })\n\n // Start fetching notifications\n store.dispatch('startFetchingNotifications')\n\n // Get user mutes\n store.dispatch('fetchMutes')\n\n // Fetch our friends\n store.rootState.api.backendInteractor.fetchFriends({ id: user.id })\n .then((friends) => commit('addNewUsers', friends))\n } else {\n const response = data.error\n // Authentication failed\n commit('endLogin')\n if (response.status === 401) {\n reject(new Error('Wrong username or password'))\n } else {\n reject(new Error('An error occurred, please try again'))\n }\n }\n commit('endLogin')\n resolve()\n })\n .catch((error) => {\n console.log(error)\n commit('endLogin')\n reject(new Error('Failed to connect to server, try again'))\n })\n })\n }\n }\n}\n\nexport default users\n","import runtime from 'serviceworker-webpack-plugin/lib/runtime'\n\nfunction urlBase64ToUint8Array (base64String) {\n const padding = '='.repeat((4 - base64String.length % 4) % 4)\n const base64 = (base64String + padding)\n .replace(/-/g, '+')\n .replace(/_/g, '/')\n\n const rawData = window.atob(base64)\n return Uint8Array.from([...rawData].map((char) => char.charCodeAt(0)))\n}\n\nfunction isPushSupported () {\n return 'serviceWorker' in navigator && 'PushManager' in window\n}\n\nfunction getOrCreateServiceWorker () {\n return runtime.register()\n .catch((err) => console.error('Unable to get or create a service worker.', err))\n}\n\nfunction subscribePush (registration, isEnabled, vapidPublicKey) {\n if (!isEnabled) return Promise.reject(new Error('Web Push is disabled in config'))\n if (!vapidPublicKey) return Promise.reject(new Error('VAPID public key is not found'))\n\n const subscribeOptions = {\n userVisibleOnly: true,\n applicationServerKey: urlBase64ToUint8Array(vapidPublicKey)\n }\n return registration.pushManager.subscribe(subscribeOptions)\n}\n\nfunction unsubscribePush (registration) {\n return registration.pushManager.getSubscription()\n .then((subscribtion) => {\n if (subscribtion === null) { return }\n return subscribtion.unsubscribe()\n })\n}\n\nfunction deleteSubscriptionFromBackEnd (token) {\n return window.fetch('/api/v1/push/subscription/', {\n method: 'DELETE',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${token}`\n }\n }).then((response) => {\n if (!response.ok) throw new Error('Bad status code from server.')\n return response\n })\n}\n\nfunction sendSubscriptionToBackEnd (subscription, token, notificationVisibility) {\n return window.fetch('/api/v1/push/subscription/', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${token}`\n },\n body: JSON.stringify({\n subscription,\n data: {\n alerts: {\n follow: notificationVisibility.follows,\n favourite: notificationVisibility.likes,\n mention: notificationVisibility.mentions,\n reblog: notificationVisibility.repeats\n }\n }\n })\n }).then((response) => {\n if (!response.ok) throw new Error('Bad status code from server.')\n return response.json()\n }).then((responseData) => {\n if (!responseData.id) throw new Error('Bad response from server.')\n return responseData\n })\n}\n\nexport function registerPushNotifications (isEnabled, vapidPublicKey, token, notificationVisibility) {\n if (isPushSupported()) {\n getOrCreateServiceWorker()\n .then((registration) => subscribePush(registration, isEnabled, vapidPublicKey))\n .then((subscription) => sendSubscriptionToBackEnd(subscription, token, notificationVisibility))\n .catch((e) => console.warn(`Failed to setup Web Push Notifications: ${e.message}`))\n }\n}\n\nexport function unregisterPushNotifications (token) {\n if (isPushSupported()) {\n Promise.all([\n deleteSubscriptionFromBackEnd(token),\n getOrCreateServiceWorker()\n .then((registration) => {\n return unsubscribePush(registration).then((result) => [registration, result])\n })\n .then(([registration, unsubResult]) => {\n if (!unsubResult) {\n console.warn('Push subscription cancellation wasn\\'t successful, killing SW anyway...')\n }\n return registration.unregister().then((result) => {\n if (!result) {\n console.warn('Failed to kill SW')\n }\n })\n })\n ]).catch((e) => console.warn(`Failed to disable Web Push Notifications: ${e.message}`))\n }\n}\n","import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js'\nimport { Socket } from 'phoenix'\n\nconst api = {\n state: {\n backendInteractor: backendInteractorService(),\n fetchers: {},\n socket: null,\n followRequests: []\n },\n mutations: {\n setBackendInteractor (state, backendInteractor) {\n state.backendInteractor = backendInteractor\n },\n addFetcher (state, { fetcherName, fetcher }) {\n state.fetchers[fetcherName] = fetcher\n },\n removeFetcher (state, { fetcherName }) {\n delete state.fetchers[fetcherName]\n },\n setWsToken (state, token) {\n state.wsToken = token\n },\n setSocket (state, socket) {\n state.socket = socket\n },\n setFollowRequests (state, value) {\n state.followRequests = value\n }\n },\n actions: {\n startFetchingTimeline (store, { timeline = 'friends', tag = false, userId = false }) {\n // Don't start fetching if we already are.\n if (store.state.fetchers[timeline]) return\n\n const fetcher = store.state.backendInteractor.startFetchingTimeline({ timeline, store, userId, tag })\n store.commit('addFetcher', { fetcherName: timeline, fetcher })\n },\n startFetchingNotifications (store) {\n // Don't start fetching if we already are.\n if (store.state.fetchers['notifications']) return\n\n const fetcher = store.state.backendInteractor.startFetchingNotifications({ store })\n store.commit('addFetcher', { fetcherName: 'notifications', fetcher })\n },\n stopFetching (store, fetcherName) {\n const fetcher = store.state.fetchers[fetcherName]\n window.clearInterval(fetcher)\n store.commit('removeFetcher', { fetcherName })\n },\n setWsToken (store, token) {\n store.commit('setWsToken', token)\n },\n initializeSocket ({ dispatch, commit, state, rootState }) {\n // Set up websocket connection\n const token = state.wsToken\n if (rootState.instance.chatAvailable && typeof token !== 'undefined' && state.socket === null) {\n const socket = new Socket('/socket', { params: { token } })\n socket.connect()\n\n commit('setSocket', socket)\n dispatch('initializeChat', socket)\n }\n },\n disconnectFromSocket ({ commit, state }) {\n state.socket && state.socket.disconnect()\n commit('setSocket', null)\n },\n removeFollowRequest (store, request) {\n let requests = store.state.followRequests.filter((it) => it !== request)\n store.commit('setFollowRequests', requests)\n }\n }\n}\n\nexport default api\n","import { set, delete as del } from 'vue'\nimport { setPreset, applyTheme } from '../services/style_setter/style_setter.js'\n\nconst browserLocale = (window.navigator.language || 'en').split('-')[0]\n\nconst defaultState = {\n colors: {},\n hideMutedPosts: undefined, // instance default\n collapseMessageWithSubject: undefined, // instance default\n hideAttachments: false,\n hideAttachmentsInConv: false,\n maxThumbnails: 16,\n hideNsfw: true,\n preloadImage: true,\n loopVideo: true,\n loopVideoSilentOnly: true,\n autoLoad: true,\n streaming: false,\n hoverPreview: true,\n autohideFloatingPostButton: false,\n pauseOnUnfocused: true,\n stopGifs: false,\n replyVisibility: 'all',\n notificationVisibility: {\n follows: true,\n mentions: true,\n likes: true,\n repeats: true\n },\n webPushNotifications: false,\n muteWords: [],\n highlight: {},\n interfaceLanguage: browserLocale,\n hideScopeNotice: false,\n scopeCopy: undefined, // instance default\n subjectLineBehavior: undefined, // instance default\n alwaysShowSubjectInput: undefined, // instance default\n postContentType: undefined, // instance default\n minimalScopesMode: undefined // instance default\n}\n\nconst config = {\n state: defaultState,\n mutations: {\n setOption (state, { name, value }) {\n set(state, name, value)\n },\n setHighlight (state, { user, color, type }) {\n const data = this.state.config.highlight[user]\n if (color || type) {\n set(state.highlight, user, { color: color || data.color, type: type || data.type })\n } else {\n del(state.highlight, user)\n }\n }\n },\n actions: {\n setHighlight ({ commit, dispatch }, { user, color, type }) {\n commit('setHighlight', { user, color, type })\n },\n setOption ({ commit, dispatch }, { name, value }) {\n commit('setOption', { name, value })\n switch (name) {\n case 'theme':\n setPreset(value, commit)\n break\n case 'customTheme':\n applyTheme(value, commit)\n }\n }\n }\n}\n\nexport default config\n","const chat = {\n state: {\n messages: [],\n channel: { state: '' }\n },\n mutations: {\n setChannel (state, channel) {\n state.channel = channel\n },\n addMessage (state, message) {\n state.messages.push(message)\n state.messages = state.messages.slice(-19, 20)\n },\n setMessages (state, messages) {\n state.messages = messages.slice(-19, 20)\n }\n },\n actions: {\n initializeChat (store, socket) {\n const channel = socket.channel('chat:public')\n channel.on('new_msg', (msg) => {\n store.commit('addMessage', msg)\n })\n channel.on('messages', ({ messages }) => {\n store.commit('setMessages', messages)\n })\n channel.join()\n store.commit('setChannel', channel)\n }\n }\n}\n\nexport default chat\n","import { delete as del } from 'vue'\n\nconst oauth = {\n state: {\n clientId: false,\n clientSecret: false,\n /* App token is authentication for app without any user, used mostly for\n * MastoAPI's registration of new users, stored so that we can fall back to\n * it on logout\n */\n appToken: false,\n /* User token is authentication for app with user, this is for every calls\n * that need authorized user to be successful (i.e. posting, liking etc)\n */\n userToken: false\n },\n mutations: {\n setClientData (state, { clientId, clientSecret }) {\n state.clientId = clientId\n state.clientSecret = clientSecret\n },\n setAppToken (state, token) {\n state.appToken = token\n },\n setToken (state, token) {\n state.userToken = token\n },\n clearToken (state) {\n state.userToken = false\n // state.token is userToken with older name, coming from persistent state\n // let's clear it as well, since it is being used as a fallback of state.userToken\n del(state, 'token')\n }\n },\n getters: {\n getToken: state => () => {\n // state.token is userToken with older name, coming from persistent state\n // added here for smoother transition, otherwise user will be logged out\n return state.userToken || state.token || state.appToken\n },\n getUserToken: state => () => {\n // state.token is userToken with older name, coming from persistent state\n // added here for smoother transition, otherwise user will be logged out\n return state.userToken || state.token\n }\n }\n}\n\nexport default oauth\n","const PASSWORD_STRATEGY = 'password'\nconst TOKEN_STRATEGY = 'token'\n\n// MFA strategies\nconst TOTP_STRATEGY = 'totp'\nconst RECOVERY_STRATEGY = 'recovery'\n\n// initial state\nconst state = {\n app: null,\n settings: {},\n strategy: PASSWORD_STRATEGY,\n initStrategy: PASSWORD_STRATEGY // default strategy from config\n}\n\nconst resetState = (state) => {\n state.strategy = state.initStrategy\n state.settings = {}\n state.app = null\n}\n\n// getters\nconst getters = {\n app: (state, getters) => {\n return state.app\n },\n settings: (state, getters) => {\n return state.settings\n },\n requiredPassword: (state, getters, rootState) => {\n return state.strategy === PASSWORD_STRATEGY\n },\n requiredToken: (state, getters, rootState) => {\n return state.strategy === TOKEN_STRATEGY\n },\n requiredTOTP: (state, getters, rootState) => {\n return state.strategy === TOTP_STRATEGY\n },\n requiredRecovery: (state, getters, rootState) => {\n return state.strategy === RECOVERY_STRATEGY\n }\n}\n\n// mutations\nconst mutations = {\n setInitialStrategy (state, strategy) {\n if (strategy) {\n state.initStrategy = strategy\n state.strategy = strategy\n }\n },\n requirePassword (state) {\n state.strategy = PASSWORD_STRATEGY\n },\n requireToken (state) {\n state.strategy = TOKEN_STRATEGY\n },\n requireMFA (state, { app, settings }) {\n state.settings = settings\n state.app = app\n state.strategy = TOTP_STRATEGY // default strategy of MFA\n },\n requireRecovery (state) {\n state.strategy = RECOVERY_STRATEGY\n },\n requireTOTP (state) {\n state.strategy = TOTP_STRATEGY\n },\n abortMFA (state) {\n resetState(state)\n }\n}\n\n// actions\nconst actions = {\n // eslint-disable-next-line camelcase\n async login ({ state, dispatch, commit }, { access_token }) {\n commit('setToken', access_token, { root: true })\n await dispatch('loginUser', access_token, { root: true })\n resetState(state)\n }\n}\n\nexport default {\n namespaced: true,\n state,\n getters,\n mutations,\n actions\n}\n","import fileTypeService from '../services/file_type/file_type.service.js'\n\nconst mediaViewer = {\n state: {\n media: [],\n currentIndex: 0,\n activated: false\n },\n mutations: {\n setMedia (state, media) {\n state.media = media\n },\n setCurrent (state, index) {\n state.activated = true\n state.currentIndex = index\n },\n close (state) {\n state.activated = false\n }\n },\n actions: {\n setMedia ({ commit }, attachments) {\n const media = attachments.filter(attachment => {\n const type = fileTypeService.fileType(attachment.mimetype)\n return type === 'image' || type === 'video'\n })\n commit('setMedia', media)\n },\n setCurrent ({ commit, state }, current) {\n const index = state.media.indexOf(current)\n commit('setCurrent', index || 0)\n },\n closeMediaViewer ({ commit }) {\n commit('close')\n }\n }\n}\n\nexport default mediaViewer\n","const oauthTokens = {\n state: {\n tokens: []\n },\n actions: {\n fetchTokens ({ rootState, commit }) {\n rootState.api.backendInteractor.fetchOAuthTokens().then((tokens) => {\n commit('swapTokens', tokens)\n })\n },\n revokeToken ({ rootState, commit, state }, id) {\n rootState.api.backendInteractor.revokeOAuthToken(id).then((response) => {\n if (response.status === 201) {\n commit('swapTokens', state.tokens.filter(token => token.id !== id))\n }\n })\n }\n },\n mutations: {\n swapTokens (state, tokens) {\n state.tokens = tokens\n }\n }\n}\n\nexport default oauthTokens\n","import filter from 'lodash/filter'\n\nconst reports = {\n state: {\n userId: null,\n statuses: [],\n modalActivated: false\n },\n mutations: {\n openUserReportingModal (state, { userId, statuses }) {\n state.userId = userId\n state.statuses = statuses\n state.modalActivated = true\n },\n closeUserReportingModal (state) {\n state.modalActivated = false\n }\n },\n actions: {\n openUserReportingModal ({ rootState, commit }, userId) {\n const statuses = filter(rootState.statuses.allStatuses, status => status.user.id === userId)\n commit('openUserReportingModal', { userId, statuses })\n },\n closeUserReportingModal ({ commit }) {\n commit('closeUserReportingModal')\n }\n }\n}\n\nexport default reports\n","import { merge } from 'lodash'\nimport { set } from 'vue'\n\nconst polls = {\n state: {\n // Contains key = id, value = number of trackers for this poll\n trackedPolls: {},\n pollsObject: {}\n },\n mutations: {\n mergeOrAddPoll (state, poll) {\n const existingPoll = state.pollsObject[poll.id]\n // Make expired-state change trigger re-renders properly\n poll.expired = Date.now() > Date.parse(poll.expires_at)\n if (existingPoll) {\n set(state.pollsObject, poll.id, merge(existingPoll, poll))\n } else {\n set(state.pollsObject, poll.id, poll)\n }\n },\n trackPoll (state, pollId) {\n const currentValue = state.trackedPolls[pollId]\n if (currentValue) {\n set(state.trackedPolls, pollId, currentValue + 1)\n } else {\n set(state.trackedPolls, pollId, 1)\n }\n },\n untrackPoll (state, pollId) {\n const currentValue = state.trackedPolls[pollId]\n if (currentValue) {\n set(state.trackedPolls, pollId, currentValue - 1)\n } else {\n set(state.trackedPolls, pollId, 0)\n }\n }\n },\n actions: {\n mergeOrAddPoll ({ commit }, poll) {\n commit('mergeOrAddPoll', poll)\n },\n updateTrackedPoll ({ rootState, dispatch, commit }, pollId) {\n rootState.api.backendInteractor.fetchPoll(pollId).then(poll => {\n setTimeout(() => {\n if (rootState.polls.trackedPolls[pollId]) {\n dispatch('updateTrackedPoll', pollId)\n }\n }, 30 * 1000)\n commit('mergeOrAddPoll', poll)\n })\n },\n trackPoll ({ rootState, commit, dispatch }, pollId) {\n if (!rootState.polls.trackedPolls[pollId]) {\n setTimeout(() => dispatch('updateTrackedPoll', pollId), 30 * 1000)\n }\n commit('trackPoll', pollId)\n },\n untrackPoll ({ commit }, pollId) {\n commit('untrackPoll', pollId)\n },\n votePoll ({ rootState, commit }, { id, pollId, choices }) {\n return rootState.api.backendInteractor.vote(pollId, choices).then(poll => {\n commit('mergeOrAddPoll', poll)\n return poll\n })\n }\n }\n}\n\nexport default polls\n","import merge from 'lodash.merge'\nimport objectPath from 'object-path'\nimport localforage from 'localforage'\nimport { each } from 'lodash'\n\nlet loaded = false\n\nconst defaultReducer = (state, paths) => (\n paths.length === 0 ? state : paths.reduce((substate, path) => {\n objectPath.set(substate, path, objectPath.get(state, path))\n return substate\n }, {})\n)\n\nconst saveImmedeatelyActions = [\n 'markNotificationsAsSeen',\n 'clearCurrentUser',\n 'setCurrentUser',\n 'setHighlight',\n 'setOption',\n 'setClientData',\n 'setToken',\n 'clearToken'\n]\n\nconst defaultStorage = (() => {\n return localforage\n})()\n\nexport default function createPersistedState ({\n key = 'vuex-lz',\n paths = [],\n getState = (key, storage) => {\n let value = storage.getItem(key)\n return value\n },\n setState = (key, state, storage) => {\n if (!loaded) {\n console.log('waiting for old state to be loaded...')\n return Promise.resolve()\n } else {\n return storage.setItem(key, state)\n }\n },\n reducer = defaultReducer,\n storage = defaultStorage,\n subscriber = store => handler => store.subscribe(handler)\n} = {}) {\n return getState(key, storage).then((savedState) => {\n return store => {\n try {\n if (savedState !== null && typeof savedState === 'object') {\n // build user cache\n const usersState = savedState.users || {}\n usersState.usersObject = {}\n const users = usersState.users || []\n each(users, (user) => { usersState.usersObject[user.id] = user })\n savedState.users = usersState\n\n store.replaceState(\n merge({}, store.state, savedState)\n )\n }\n loaded = true\n } catch (e) {\n console.log(\"Couldn't load state\")\n console.error(e)\n loaded = true\n }\n subscriber(store)((mutation, state) => {\n try {\n if (saveImmedeatelyActions.includes(mutation.type)) {\n setState(key, reducer(state, paths), storage)\n .then(success => {\n if (typeof success !== 'undefined') {\n if (mutation.type === 'setOption' || mutation.type === 'setCurrentUser') {\n store.dispatch('settingsSaved', { success })\n }\n }\n }, error => {\n if (mutation.type === 'setOption' || mutation.type === 'setCurrentUser') {\n store.dispatch('settingsSaved', { error })\n }\n })\n }\n } catch (e) {\n console.log(\"Couldn't persist state:\")\n console.log(e)\n }\n })\n }\n })\n}\n","export default (store) => {\n store.subscribe((mutation, state) => {\n const vapidPublicKey = state.instance.vapidPublicKey\n const webPushNotification = state.config.webPushNotifications\n const permission = state.interface.notificationPermission === 'granted'\n const user = state.users.currentUser\n\n const isUserMutation = mutation.type === 'setCurrentUser'\n const isVapidMutation = mutation.type === 'setInstanceOption' && mutation.payload.name === 'vapidPublicKey'\n const isPermMutation = mutation.type === 'setNotificationPermission' && mutation.payload === 'granted'\n const isUserConfigMutation = mutation.type === 'setOption' && mutation.payload.name === 'webPushNotifications'\n const isVisibilityMutation = mutation.type === 'setOption' && mutation.payload.name === 'notificationVisibility'\n\n if (isUserMutation || isVapidMutation || isPermMutation || isUserConfigMutation || isVisibilityMutation) {\n if (user && vapidPublicKey && permission && webPushNotification) {\n return store.dispatch('registerPushNotifications')\n } else if (isUserConfigMutation && !webPushNotification) {\n return store.dispatch('unregisterPushNotifications')\n }\n }\n })\n}\n","import Vue from 'vue'\nimport VueRouter from 'vue-router'\nimport routes from './routes'\nimport App from '../App.vue'\nimport { windowWidth } from '../services/window_utils/window_utils'\nimport { getOrCreateApp, getClientToken } from '../services/new_api/oauth.js'\nimport backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js'\n\nconst getStatusnetConfig = async ({ store }) => {\n try {\n const res = await window.fetch('/api/statusnet/config.json')\n if (res.ok) {\n const data = await res.json()\n const { name, closed: registrationClosed, textlimit, uploadlimit, server, vapidPublicKey, safeDMMentionsEnabled } = data.site\n\n store.dispatch('setInstanceOption', { name: 'name', value: name })\n store.dispatch('setInstanceOption', { name: 'registrationOpen', value: (registrationClosed === '0') })\n store.dispatch('setInstanceOption', { name: 'textlimit', value: parseInt(textlimit) })\n store.dispatch('setInstanceOption', { name: 'server', value: server })\n store.dispatch('setInstanceOption', { name: 'safeDM', value: safeDMMentionsEnabled !== '0' })\n\n // TODO: default values for this stuff, added if to not make it break on\n // my dev config out of the box.\n if (uploadlimit) {\n store.dispatch('setInstanceOption', { name: 'uploadlimit', value: parseInt(uploadlimit.uploadlimit) })\n store.dispatch('setInstanceOption', { name: 'avatarlimit', value: parseInt(uploadlimit.avatarlimit) })\n store.dispatch('setInstanceOption', { name: 'backgroundlimit', value: parseInt(uploadlimit.backgroundlimit) })\n store.dispatch('setInstanceOption', { name: 'bannerlimit', value: parseInt(uploadlimit.bannerlimit) })\n }\n\n if (vapidPublicKey) {\n store.dispatch('setInstanceOption', { name: 'vapidPublicKey', value: vapidPublicKey })\n }\n\n return data.site.pleromafe\n } else {\n throw (res)\n }\n } catch (error) {\n console.error('Could not load statusnet config, potentially fatal')\n console.error(error)\n }\n}\n\nconst getStaticConfig = async () => {\n try {\n const res = await window.fetch('/static/config.json')\n if (res.ok) {\n return res.json()\n } else {\n throw (res)\n }\n } catch (error) {\n console.warn('Failed to load static/config.json, continuing without it.')\n console.warn(error)\n return {}\n }\n}\n\nconst setSettings = async ({ apiConfig, staticConfig, store }) => {\n const overrides = window.___pleromafe_dev_overrides || {}\n const env = window.___pleromafe_mode.NODE_ENV\n\n // This takes static config and overrides properties that are present in apiConfig\n let config = {}\n if (overrides.staticConfigPreference && env === 'development') {\n console.warn('OVERRIDING API CONFIG WITH STATIC CONFIG')\n config = Object.assign({}, apiConfig, staticConfig)\n } else {\n config = Object.assign({}, staticConfig, apiConfig)\n }\n\n const copyInstanceOption = (name) => {\n store.dispatch('setInstanceOption', { name, value: config[name] })\n }\n\n copyInstanceOption('nsfwCensorImage')\n copyInstanceOption('background')\n copyInstanceOption('hidePostStats')\n copyInstanceOption('hideUserStats')\n copyInstanceOption('hideFilteredStatuses')\n copyInstanceOption('logo')\n\n store.dispatch('setInstanceOption', {\n name: 'logoMask',\n value: typeof config.logoMask === 'undefined'\n ? true\n : config.logoMask\n })\n\n store.dispatch('setInstanceOption', {\n name: 'logoMargin',\n value: typeof config.logoMargin === 'undefined'\n ? 0\n : config.logoMargin\n })\n store.commit('authFlow/setInitialStrategy', config.loginMethod)\n\n copyInstanceOption('redirectRootNoLogin')\n copyInstanceOption('redirectRootLogin')\n copyInstanceOption('showInstanceSpecificPanel')\n copyInstanceOption('minimalScopesMode')\n copyInstanceOption('hideMutedPosts')\n copyInstanceOption('collapseMessageWithSubject')\n copyInstanceOption('scopeCopy')\n copyInstanceOption('subjectLineBehavior')\n copyInstanceOption('postContentType')\n copyInstanceOption('alwaysShowSubjectInput')\n copyInstanceOption('noAttachmentLinks')\n copyInstanceOption('showFeaturesPanel')\n\n return store.dispatch('setTheme', config['theme'])\n}\n\nconst getTOS = async ({ store }) => {\n try {\n const res = await window.fetch('/static/terms-of-service.html')\n if (res.ok) {\n const html = await res.text()\n store.dispatch('setInstanceOption', { name: 'tos', value: html })\n } else {\n throw (res)\n }\n } catch (e) {\n console.warn(\"Can't load TOS\")\n console.warn(e)\n }\n}\n\nconst getInstancePanel = async ({ store }) => {\n try {\n const res = await window.fetch('/instance/panel.html')\n if (res.ok) {\n const html = await res.text()\n store.dispatch('setInstanceOption', { name: 'instanceSpecificPanelContent', value: html })\n } else {\n throw (res)\n }\n } catch (e) {\n console.warn(\"Can't load instance panel\")\n console.warn(e)\n }\n}\n\nconst getStickers = async ({ store }) => {\n try {\n const res = await window.fetch('/static/stickers.json')\n if (res.ok) {\n const values = await res.json()\n const stickers = (await Promise.all(\n Object.entries(values).map(async ([name, path]) => {\n const resPack = await window.fetch(path + 'pack.json')\n var meta = {}\n if (resPack.ok) {\n meta = await resPack.json()\n }\n return {\n pack: name,\n path,\n meta\n }\n })\n )).sort((a, b) => {\n return a.meta.title.localeCompare(b.meta.title)\n })\n store.dispatch('setInstanceOption', { name: 'stickers', value: stickers })\n } else {\n throw (res)\n }\n } catch (e) {\n console.warn(\"Can't load stickers\")\n console.warn(e)\n }\n}\n\nconst getStaticEmoji = async ({ store }) => {\n try {\n const res = await window.fetch('/static/emoji.json')\n if (res.ok) {\n const values = await res.json()\n const emoji = Object.keys(values).map((key) => {\n return {\n displayText: key,\n imageUrl: false,\n replacement: values[key]\n }\n })\n store.dispatch('setInstanceOption', { name: 'emoji', value: emoji })\n } else {\n throw (res)\n }\n } catch (e) {\n console.warn(\"Can't load static emoji\")\n console.warn(e)\n }\n}\n\n// This is also used to indicate if we have a 'pleroma backend' or not.\n// Somewhat weird, should probably be somewhere else.\nconst getCustomEmoji = async ({ store }) => {\n try {\n const res = await window.fetch('/api/pleroma/emoji.json')\n if (res.ok) {\n const result = await res.json()\n const values = Array.isArray(result) ? Object.assign({}, ...result) : result\n const emoji = Object.keys(values).map((key) => {\n const imageUrl = values[key].image_url\n return {\n displayText: key,\n imageUrl: imageUrl ? store.state.instance.server + imageUrl : values[key],\n replacement: `:${key}: `\n }\n })\n store.dispatch('setInstanceOption', { name: 'customEmoji', value: emoji })\n store.dispatch('setInstanceOption', { name: 'pleromaBackend', value: true })\n } else {\n throw (res)\n }\n } catch (e) {\n store.dispatch('setInstanceOption', { name: 'pleromaBackend', value: false })\n console.warn(\"Can't load custom emojis, maybe not a Pleroma instance?\")\n console.warn(e)\n }\n}\n\nconst getAppSecret = async ({ store }) => {\n const { state, commit } = store\n const { oauth, instance } = state\n return getOrCreateApp({ ...oauth, instance: instance.server, commit })\n .then((app) => getClientToken({ ...app, instance: instance.server }))\n .then((token) => {\n commit('setAppToken', token.access_token)\n commit('setBackendInteractor', backendInteractorService(store.getters.getToken()))\n })\n}\n\nconst getNodeInfo = async ({ store }) => {\n try {\n const res = await window.fetch('/nodeinfo/2.0.json')\n if (res.ok) {\n const data = await res.json()\n const metadata = data.metadata\n const features = metadata.features\n store.dispatch('setInstanceOption', { name: 'mediaProxyAvailable', value: features.includes('media_proxy') })\n store.dispatch('setInstanceOption', { name: 'chatAvailable', value: features.includes('chat') })\n store.dispatch('setInstanceOption', { name: 'gopherAvailable', value: features.includes('gopher') })\n store.dispatch('setInstanceOption', { name: 'pollsAvailable', value: features.includes('polls') })\n store.dispatch('setInstanceOption', { name: 'pollLimits', value: metadata.pollLimits })\n store.dispatch('setInstanceOption', { name: 'mailerEnabled', value: metadata.mailerEnabled })\n\n store.dispatch('setInstanceOption', { name: 'restrictedNicknames', value: metadata.restrictedNicknames })\n store.dispatch('setInstanceOption', { name: 'postFormats', value: metadata.postFormats })\n\n const suggestions = metadata.suggestions\n store.dispatch('setInstanceOption', { name: 'suggestionsEnabled', value: suggestions.enabled })\n store.dispatch('setInstanceOption', { name: 'suggestionsWeb', value: suggestions.web })\n\n const software = data.software\n store.dispatch('setInstanceOption', { name: 'backendVersion', value: software.version })\n\n const frontendVersion = window.___pleromafe_commit_hash\n store.dispatch('setInstanceOption', { name: 'frontendVersion', value: frontendVersion })\n store.dispatch('setInstanceOption', { name: 'tagPolicyAvailable', value: metadata.federation.mrf_policies.includes('TagPolicy') })\n } else {\n throw (res)\n }\n } catch (e) {\n console.warn('Could not load nodeinfo')\n console.warn(e)\n }\n}\n\nconst setConfig = async ({ store }) => {\n // apiConfig, staticConfig\n const configInfos = await Promise.all([getStatusnetConfig({ store }), getStaticConfig()])\n const apiConfig = configInfos[0]\n const staticConfig = configInfos[1]\n\n await setSettings({ store, apiConfig, staticConfig }).then(getAppSecret({ store }))\n}\n\nconst checkOAuthToken = async ({ store }) => {\n return new Promise(async (resolve, reject) => {\n if (store.getters.getUserToken()) {\n try {\n await store.dispatch('loginUser', store.getters.getUserToken())\n } catch (e) {\n console.log(e)\n }\n }\n resolve()\n })\n}\n\nconst afterStoreSetup = async ({ store, i18n }) => {\n if (store.state.config.customTheme) {\n // This is a hack to deal with async loading of config.json and themes\n // See: style_setter.js, setPreset()\n window.themeLoaded = true\n store.dispatch('setOption', {\n name: 'customTheme',\n value: store.state.config.customTheme\n })\n }\n\n const width = windowWidth()\n store.dispatch('setMobileLayout', width <= 800)\n\n // Now we can try getting the server settings and logging in\n await Promise.all([\n checkOAuthToken({ store }),\n setConfig({ store }),\n getTOS({ store }),\n getInstancePanel({ store }),\n getStickers({ store }),\n getStaticEmoji({ store }),\n getCustomEmoji({ store }),\n getNodeInfo({ store })\n ])\n\n const router = new VueRouter({\n mode: 'history',\n routes: routes(store),\n scrollBehavior: (to, _from, savedPosition) => {\n if (to.matched.some(m => m.meta.dontScroll)) {\n return false\n }\n return savedPosition || { x: 0, y: 0 }\n }\n })\n\n /* eslint-disable no-new */\n return new Vue({\n router,\n store,\n i18n,\n el: '#app',\n render: h => h(App)\n })\n}\n\nexport default afterStoreSetup\n","import PublicTimeline from 'components/public_timeline/public_timeline.vue'\nimport PublicAndExternalTimeline from 'components/public_and_external_timeline/public_and_external_timeline.vue'\nimport FriendsTimeline from 'components/friends_timeline/friends_timeline.vue'\nimport TagTimeline from 'components/tag_timeline/tag_timeline.vue'\nimport ConversationPage from 'components/conversation-page/conversation-page.vue'\nimport Interactions from 'components/interactions/interactions.vue'\nimport DMs from 'components/dm_timeline/dm_timeline.vue'\nimport UserProfile from 'components/user_profile/user_profile.vue'\nimport Search from 'components/search/search.vue'\nimport Settings from 'components/settings/settings.vue'\nimport Registration from 'components/registration/registration.vue'\nimport PasswordReset from 'components/password_reset/password_reset.vue'\nimport UserSettings from 'components/user_settings/user_settings.vue'\nimport FollowRequests from 'components/follow_requests/follow_requests.vue'\nimport OAuthCallback from 'components/oauth_callback/oauth_callback.vue'\nimport Notifications from 'components/notifications/notifications.vue'\nimport AuthForm from 'components/auth_form/auth_form.js'\nimport ChatPanel from 'components/chat_panel/chat_panel.vue'\nimport WhoToFollow from 'components/who_to_follow/who_to_follow.vue'\nimport About from 'components/about/about.vue'\n\nexport default (store) => {\n const validateAuthenticatedRoute = (to, from, next) => {\n if (store.state.users.currentUser) {\n next()\n } else {\n next(store.state.instance.redirectRootNoLogin || '/main/all')\n }\n }\n\n return [\n { name: 'root',\n path: '/',\n redirect: _to => {\n return (store.state.users.currentUser\n ? store.state.instance.redirectRootLogin\n : store.state.instance.redirectRootNoLogin) || '/main/all'\n }\n },\n { name: 'public-external-timeline', path: '/main/all', component: PublicAndExternalTimeline },\n { name: 'public-timeline', path: '/main/public', component: PublicTimeline },\n { name: 'friends', path: '/main/friends', component: FriendsTimeline, beforeEnter: validateAuthenticatedRoute },\n { name: 'tag-timeline', path: '/tag/:tag', component: TagTimeline },\n { name: 'conversation', path: '/notice/:id', component: ConversationPage, meta: { dontScroll: true } },\n { name: 'external-user-profile', path: '/users/:id', component: UserProfile },\n { name: 'interactions', path: '/users/:username/interactions', component: Interactions, beforeEnter: validateAuthenticatedRoute },\n { name: 'dms', path: '/users/:username/dms', component: DMs, beforeEnter: validateAuthenticatedRoute },\n { name: 'settings', path: '/settings', component: Settings },\n { name: 'registration', path: '/registration', component: Registration },\n { name: 'password-reset', path: '/password-reset', component: PasswordReset },\n { name: 'registration-token', path: '/registration/:token', component: Registration },\n { name: 'friend-requests', path: '/friend-requests', component: FollowRequests, beforeEnter: validateAuthenticatedRoute },\n { name: 'user-settings', path: '/user-settings', component: UserSettings, beforeEnter: validateAuthenticatedRoute },\n { name: 'notifications', path: '/:username/notifications', component: Notifications, beforeEnter: validateAuthenticatedRoute },\n { name: 'login', path: '/login', component: AuthForm },\n { name: 'chat', path: '/chat', component: ChatPanel, props: () => ({ floating: false }) },\n { name: 'oauth-callback', path: '/oauth-callback', component: OAuthCallback, props: (route) => ({ code: route.query.code }) },\n { name: 'search', path: '/search', component: Search, props: (route) => ({ query: route.query.query }) },\n { name: 'who-to-follow', path: '/who-to-follow', component: WhoToFollow, beforeEnter: validateAuthenticatedRoute },\n { name: 'about', path: '/about', component: About },\n { name: 'user-profile', path: '/(users/)?:name', component: UserProfile }\n ]\n}\n","/* script */\nexport * from \"!!babel-loader!./public_timeline.js\"\nimport __vue_script__ from \"!!babel-loader!./public_timeline.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-5f2a502e\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./public_timeline.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","// style-loader: Adds some css to the DOM by adding a