revert 4a94c9a31e
revert Add ability to follow hashtags (#336) Co-authored-by: FloatingGhost <hannah@coffee-and-dreams.uk> Reviewed-on: https://akkoma.dev/AkkomaGang/akkoma/pulls/336
This commit is contained in:
parent
4c3971aebd
commit
ec6bf8c3f7
|
@ -471,15 +471,9 @@ def run(["blocking", nickname]) do
|
||||||
|
|
||||||
def run(["timeline_query", nickname]) do
|
def run(["timeline_query", nickname]) do
|
||||||
start_pleroma()
|
start_pleroma()
|
||||||
|
|
||||||
params = %{local: true}
|
params = %{local: true}
|
||||||
|
|
||||||
with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do
|
with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do
|
||||||
followed_hashtags =
|
|
||||||
user
|
|
||||||
|> User.followed_hashtags()
|
|
||||||
|> Enum.map(& &1.id)
|
|
||||||
|
|
||||||
params =
|
params =
|
||||||
params
|
params
|
||||||
|> Map.put(:type, ["Create", "Announce"])
|
|> Map.put(:type, ["Create", "Announce"])
|
||||||
|
@ -490,7 +484,6 @@ def run(["timeline_query", nickname]) do
|
||||||
|> Map.put(:announce_filtering_user, user)
|
|> Map.put(:announce_filtering_user, user)
|
||||||
|> Map.put(:user, user)
|
|> Map.put(:user, user)
|
||||||
|> Map.put(:local_only, params[:local])
|
|> Map.put(:local_only, params[:local])
|
||||||
|> Map.put(:hashtags, followed_hashtags)
|
|
||||||
|> Map.delete(:local)
|
|> Map.delete(:local)
|
||||||
|
|
||||||
_activities =
|
_activities =
|
||||||
|
|
|
@ -10,7 +10,6 @@ defmodule Pleroma.Hashtag do
|
||||||
|
|
||||||
alias Ecto.Multi
|
alias Ecto.Multi
|
||||||
alias Pleroma.Hashtag
|
alias Pleroma.Hashtag
|
||||||
alias Pleroma.User.HashtagFollow
|
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
|
||||||
|
@ -28,14 +27,6 @@ def normalize_name(name) do
|
||||||
|> String.trim()
|
|> String.trim()
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_by_id(id) do
|
|
||||||
Repo.get(Hashtag, id)
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_by_name(name) do
|
|
||||||
Repo.get_by(Hashtag, name: normalize_name(name))
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_or_create_by_name(name) do
|
def get_or_create_by_name(name) do
|
||||||
changeset = changeset(%Hashtag{}, %{name: name})
|
changeset = changeset(%Hashtag{}, %{name: name})
|
||||||
|
|
||||||
|
@ -112,22 +103,4 @@ def delete_unreferenced(ids) do
|
||||||
{:ok, deleted_count}
|
{:ok, deleted_count}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_followers(%Hashtag{id: hashtag_id}) do
|
|
||||||
from(hf in HashtagFollow)
|
|
||||||
|> where([hf], hf.hashtag_id == ^hashtag_id)
|
|
||||||
|> join(:inner, [hf], u in assoc(hf, :user))
|
|
||||||
|> select([hf, u], u.id)
|
|
||||||
|> Repo.all()
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_recipients_for_activity(%Pleroma.Activity{object: %{hashtags: tags}})
|
|
||||||
when is_list(tags) do
|
|
||||||
tags
|
|
||||||
|> Enum.map(&get_followers/1)
|
|
||||||
|> List.flatten()
|
|
||||||
|> Enum.uniq()
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_recipients_for_activity(_activity), do: []
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,8 +18,6 @@ defmodule Pleroma.User do
|
||||||
alias Pleroma.Emoji
|
alias Pleroma.Emoji
|
||||||
alias Pleroma.FollowingRelationship
|
alias Pleroma.FollowingRelationship
|
||||||
alias Pleroma.Formatter
|
alias Pleroma.Formatter
|
||||||
alias Pleroma.Hashtag
|
|
||||||
alias Pleroma.User.HashtagFollow
|
|
||||||
alias Pleroma.HTML
|
alias Pleroma.HTML
|
||||||
alias Pleroma.Keys
|
alias Pleroma.Keys
|
||||||
alias Pleroma.MFA
|
alias Pleroma.MFA
|
||||||
|
@ -170,12 +168,6 @@ defmodule Pleroma.User do
|
||||||
|
|
||||||
has_many(:frontend_profiles, Pleroma.Akkoma.FrontendSettingsProfile)
|
has_many(:frontend_profiles, Pleroma.Akkoma.FrontendSettingsProfile)
|
||||||
|
|
||||||
many_to_many(:followed_hashtags, Hashtag,
|
|
||||||
on_replace: :delete,
|
|
||||||
on_delete: :delete_all,
|
|
||||||
join_through: HashtagFollow
|
|
||||||
)
|
|
||||||
|
|
||||||
for {relationship_type,
|
for {relationship_type,
|
||||||
[
|
[
|
||||||
{outgoing_relation, outgoing_relation_target},
|
{outgoing_relation, outgoing_relation_target},
|
||||||
|
@ -2558,54 +2550,4 @@ def update_last_status_at(user) do
|
||||||
_ -> {:error, user}
|
_ -> {:error, user}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp maybe_load_followed_hashtags(%User{followed_hashtags: follows} = user)
|
|
||||||
when is_list(follows),
|
|
||||||
do: user
|
|
||||||
|
|
||||||
defp maybe_load_followed_hashtags(%User{} = user) do
|
|
||||||
followed_hashtags = HashtagFollow.get_by_user(user)
|
|
||||||
%{user | followed_hashtags: followed_hashtags}
|
|
||||||
end
|
|
||||||
|
|
||||||
def followed_hashtags(%User{followed_hashtags: follows})
|
|
||||||
when is_list(follows),
|
|
||||||
do: follows
|
|
||||||
|
|
||||||
def followed_hashtags(%User{} = user) do
|
|
||||||
{:ok, user} =
|
|
||||||
user
|
|
||||||
|> maybe_load_followed_hashtags()
|
|
||||||
|> set_cache()
|
|
||||||
|
|
||||||
user.followed_hashtags
|
|
||||||
end
|
|
||||||
|
|
||||||
def follow_hashtag(%User{} = user, %Hashtag{} = hashtag) do
|
|
||||||
Logger.debug("Follow hashtag #{hashtag.name} for user #{user.nickname}")
|
|
||||||
user = maybe_load_followed_hashtags(user)
|
|
||||||
|
|
||||||
with {:ok, _} <- HashtagFollow.new(user, hashtag),
|
|
||||||
follows <- HashtagFollow.get_by_user(user),
|
|
||||||
%User{} = user <- user |> Map.put(:followed_hashtags, follows) do
|
|
||||||
user
|
|
||||||
|> set_cache()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def unfollow_hashtag(%User{} = user, %Hashtag{} = hashtag) do
|
|
||||||
Logger.debug("Unfollow hashtag #{hashtag.name} for user #{user.nickname}")
|
|
||||||
user = maybe_load_followed_hashtags(user)
|
|
||||||
|
|
||||||
with {:ok, _} <- HashtagFollow.delete(user, hashtag),
|
|
||||||
follows <- HashtagFollow.get_by_user(user),
|
|
||||||
%User{} = user <- user |> Map.put(:followed_hashtags, follows) do
|
|
||||||
user
|
|
||||||
|> set_cache()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def following_hashtag?(%User{} = user, %Hashtag{} = hashtag) do
|
|
||||||
not is_nil(HashtagFollow.get(user, hashtag))
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
defmodule Pleroma.User.HashtagFollow do
|
|
||||||
use Ecto.Schema
|
|
||||||
import Ecto.Query
|
|
||||||
import Ecto.Changeset
|
|
||||||
|
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Hashtag
|
|
||||||
alias Pleroma.Repo
|
|
||||||
|
|
||||||
schema "user_follows_hashtag" do
|
|
||||||
belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
|
|
||||||
belongs_to(:hashtag, Hashtag)
|
|
||||||
end
|
|
||||||
|
|
||||||
def changeset(%__MODULE__{} = user_hashtag_follow, attrs) do
|
|
||||||
user_hashtag_follow
|
|
||||||
|> cast(attrs, [:user_id, :hashtag_id])
|
|
||||||
|> unique_constraint(:hashtag_id,
|
|
||||||
name: :user_hashtag_follows_user_id_hashtag_id_index,
|
|
||||||
message: "already following"
|
|
||||||
)
|
|
||||||
|> validate_required([:user_id, :hashtag_id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def new(%User{} = user, %Hashtag{} = hashtag) do
|
|
||||||
%__MODULE__{}
|
|
||||||
|> changeset(%{user_id: user.id, hashtag_id: hashtag.id})
|
|
||||||
|> Repo.insert(on_conflict: :nothing)
|
|
||||||
end
|
|
||||||
|
|
||||||
def delete(%User{} = user, %Hashtag{} = hashtag) do
|
|
||||||
with %__MODULE__{} = user_hashtag_follow <- get(user, hashtag) do
|
|
||||||
Repo.delete(user_hashtag_follow)
|
|
||||||
else
|
|
||||||
_ -> {:ok, nil}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def get(%User{} = user, %Hashtag{} = hashtag) do
|
|
||||||
from(hf in __MODULE__)
|
|
||||||
|> where([hf], hf.user_id == ^user.id and hf.hashtag_id == ^hashtag.id)
|
|
||||||
|> Repo.one()
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_by_user(%User{} = user) do
|
|
||||||
Ecto.assoc(user, :followed_hashtags)
|
|
||||||
|> Repo.all()
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -933,29 +933,6 @@ defp restrict_recipients(query, recipients, user) do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Essentially, either look for activities addressed to `recipients`, _OR_ ones
|
|
||||||
# that reference a hashtag that the user follows
|
|
||||||
# Firstly, two fallbacks in case there's no hashtag constraint, or the user doesn't
|
|
||||||
# follow any
|
|
||||||
defp restrict_recipients_or_hashtags(query, recipients, user, nil) do
|
|
||||||
restrict_recipients(query, recipients, user)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp restrict_recipients_or_hashtags(query, recipients, user, []) do
|
|
||||||
restrict_recipients(query, recipients, user)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp restrict_recipients_or_hashtags(query, recipients, _user, hashtag_ids) do
|
|
||||||
from(
|
|
||||||
[activity, object] in query,
|
|
||||||
join: hto in "hashtags_objects",
|
|
||||||
on: hto.object_id == object.id,
|
|
||||||
where:
|
|
||||||
(hto.hashtag_id in ^hashtag_ids and ^Constants.as_public() in activity.recipients) or
|
|
||||||
fragment("? && ?", ^recipients, activity.recipients)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp restrict_local(query, %{local_only: true}) do
|
defp restrict_local(query, %{local_only: true}) do
|
||||||
from(activity in query, where: activity.local == true)
|
from(activity in query, where: activity.local == true)
|
||||||
end
|
end
|
||||||
|
@ -1403,7 +1380,7 @@ def fetch_activities_query(recipients, opts \\ %{}) do
|
||||||
|> maybe_preload_report_notes(opts)
|
|> maybe_preload_report_notes(opts)
|
||||||
|> maybe_set_thread_muted_field(opts)
|
|> maybe_set_thread_muted_field(opts)
|
||||||
|> maybe_order(opts)
|
|> maybe_order(opts)
|
||||||
|> restrict_recipients_or_hashtags(recipients, opts[:user], opts[:followed_hashtags])
|
|> restrict_recipients(recipients, opts[:user])
|
||||||
|> restrict_replies(opts)
|
|> restrict_replies(opts)
|
||||||
|> restrict_since(opts)
|
|> restrict_since(opts)
|
||||||
|> restrict_local(opts)
|
|> restrict_local(opts)
|
||||||
|
|
|
@ -1,65 +0,0 @@
|
||||||
defmodule Pleroma.Web.ApiSpec.TagOperation do
|
|
||||||
alias OpenApiSpex.Operation
|
|
||||||
alias OpenApiSpex.Schema
|
|
||||||
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
|
||||||
alias Pleroma.Web.ApiSpec.Schemas.Tag
|
|
||||||
|
|
||||||
def open_api_operation(action) do
|
|
||||||
operation = String.to_existing_atom("#{action}_operation")
|
|
||||||
apply(__MODULE__, operation, [])
|
|
||||||
end
|
|
||||||
|
|
||||||
def show_operation do
|
|
||||||
%Operation{
|
|
||||||
tags: ["Tags"],
|
|
||||||
summary: "Hashtag",
|
|
||||||
description: "View a hashtag",
|
|
||||||
security: [%{"oAuth" => ["read"]}],
|
|
||||||
parameters: [id_param()],
|
|
||||||
operationId: "TagController.show",
|
|
||||||
responses: %{
|
|
||||||
200 => Operation.response("Hashtag", "application/json", Tag),
|
|
||||||
404 => Operation.response("Not Found", "application/json", ApiError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def follow_operation do
|
|
||||||
%Operation{
|
|
||||||
tags: ["Tags"],
|
|
||||||
summary: "Follow a hashtag",
|
|
||||||
description: "Follow a hashtag",
|
|
||||||
security: [%{"oAuth" => ["write:follows"]}],
|
|
||||||
parameters: [id_param()],
|
|
||||||
operationId: "TagController.follow",
|
|
||||||
responses: %{
|
|
||||||
200 => Operation.response("Hashtag", "application/json", Tag),
|
|
||||||
404 => Operation.response("Not Found", "application/json", ApiError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def unfollow_operation do
|
|
||||||
%Operation{
|
|
||||||
tags: ["Tags"],
|
|
||||||
summary: "Unfollow a hashtag",
|
|
||||||
description: "Unfollow a hashtag",
|
|
||||||
security: [%{"oAuth" => ["write:follow"]}],
|
|
||||||
parameters: [id_param()],
|
|
||||||
operationId: "TagController.unfollow",
|
|
||||||
responses: %{
|
|
||||||
200 => Operation.response("Hashtag", "application/json", Tag),
|
|
||||||
404 => Operation.response("Not Found", "application/json", ApiError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
defp id_param do
|
|
||||||
Operation.parameter(
|
|
||||||
:id,
|
|
||||||
:path,
|
|
||||||
%Schema{type: :string},
|
|
||||||
"Name of the hashtag"
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -17,16 +17,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Tag do
|
||||||
type: :string,
|
type: :string,
|
||||||
format: :uri,
|
format: :uri,
|
||||||
description: "A link to the hashtag on the instance"
|
description: "A link to the hashtag on the instance"
|
||||||
},
|
|
||||||
following: %Schema{
|
|
||||||
type: :boolean,
|
|
||||||
description: "Whether the authenticated user is following the hashtag"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
example: %{
|
example: %{
|
||||||
name: "cofe",
|
name: "cofe",
|
||||||
url: "https://lain.com/tag/cofe",
|
url: "https://lain.com/tag/cofe"
|
||||||
following: false
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
defmodule Pleroma.Web.MastodonAPI.TagController do
|
|
||||||
@moduledoc "Hashtag routes for mastodon API"
|
|
||||||
use Pleroma.Web, :controller
|
|
||||||
|
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Hashtag
|
|
||||||
|
|
||||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
|
||||||
plug(Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: ["read"]} when action in [:show])
|
|
||||||
|
|
||||||
plug(
|
|
||||||
Pleroma.Web.Plugs.OAuthScopesPlug,
|
|
||||||
%{scopes: ["write:follows"]} when action in [:follow, :unfollow]
|
|
||||||
)
|
|
||||||
|
|
||||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.TagOperation
|
|
||||||
|
|
||||||
def show(conn, %{id: id}) do
|
|
||||||
with %Hashtag{} = hashtag <- Hashtag.get_by_name(id) do
|
|
||||||
render(conn, "show.json", tag: hashtag, for_user: conn.assigns.user)
|
|
||||||
else
|
|
||||||
_ -> conn |> render_error(:not_found, "Hashtag not found")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def follow(conn, %{id: id}) do
|
|
||||||
with %Hashtag{} = hashtag <- Hashtag.get_by_name(id),
|
|
||||||
%User{} = user <- conn.assigns.user,
|
|
||||||
{:ok, _} <-
|
|
||||||
User.follow_hashtag(user, hashtag) do
|
|
||||||
render(conn, "show.json", tag: hashtag, for_user: user)
|
|
||||||
else
|
|
||||||
_ -> render_error(conn, :not_found, "Hashtag not found")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def unfollow(conn, %{id: id}) do
|
|
||||||
with %Hashtag{} = hashtag <- Hashtag.get_by_name(id),
|
|
||||||
%User{} = user <- conn.assigns.user,
|
|
||||||
{:ok, _} <-
|
|
||||||
User.unfollow_hashtag(user, hashtag) do
|
|
||||||
render(conn, "show.json", tag: hashtag, for_user: user)
|
|
||||||
else
|
|
||||||
_ -> render_error(conn, :not_found, "Hashtag not found")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -41,11 +41,6 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|
||||||
|
|
||||||
# GET /api/v1/timelines/home
|
# GET /api/v1/timelines/home
|
||||||
def home(%{assigns: %{user: user}} = conn, params) do
|
def home(%{assigns: %{user: user}} = conn, params) do
|
||||||
followed_hashtags =
|
|
||||||
user
|
|
||||||
|> User.followed_hashtags()
|
|
||||||
|> Enum.map(& &1.id)
|
|
||||||
|
|
||||||
params =
|
params =
|
||||||
params
|
params
|
||||||
|> Map.put(:type, ["Create", "Announce"])
|
|> Map.put(:type, ["Create", "Announce"])
|
||||||
|
@ -55,7 +50,6 @@ def home(%{assigns: %{user: user}} = conn, params) do
|
||||||
|> Map.put(:announce_filtering_user, user)
|
|> Map.put(:announce_filtering_user, user)
|
||||||
|> Map.put(:user, user)
|
|> Map.put(:user, user)
|
||||||
|> Map.put(:local_only, params[:local])
|
|> Map.put(:local_only, params[:local])
|
||||||
|> Map.put(:followed_hashtags, followed_hashtags)
|
|
||||||
|> Map.delete(:local)
|
|> Map.delete(:local)
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
defmodule Pleroma.Web.MastodonAPI.TagView do
|
|
||||||
use Pleroma.Web, :view
|
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Web.Router.Helpers
|
|
||||||
|
|
||||||
def render("show.json", %{tag: tag, for_user: user}) do
|
|
||||||
following =
|
|
||||||
with %User{} <- user do
|
|
||||||
User.following_hashtag?(user, tag)
|
|
||||||
else
|
|
||||||
_ -> false
|
|
||||||
end
|
|
||||||
|
|
||||||
%{
|
|
||||||
name: tag.name,
|
|
||||||
url: Helpers.tag_feed_url(Pleroma.Web.Endpoint, :feed, tag.name),
|
|
||||||
history: [],
|
|
||||||
following: following
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -598,10 +598,6 @@ defmodule Pleroma.Web.Router do
|
||||||
|
|
||||||
get("/announcements", AnnouncementController, :index)
|
get("/announcements", AnnouncementController, :index)
|
||||||
post("/announcements/:id/dismiss", AnnouncementController, :mark_read)
|
post("/announcements/:id/dismiss", AnnouncementController, :mark_read)
|
||||||
|
|
||||||
get("/tags/:id", TagController, :show)
|
|
||||||
post("/tags/:id/follow", TagController, :follow)
|
|
||||||
post("/tags/:id/unfollow", TagController, :unfollow)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api/web", Pleroma.Web do
|
scope "/api/web", Pleroma.Web do
|
||||||
|
|
|
@ -17,7 +17,6 @@ defmodule Pleroma.Web.Streamer do
|
||||||
alias Pleroma.Web.OAuth.Token
|
alias Pleroma.Web.OAuth.Token
|
||||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||||
alias Pleroma.Web.StreamerView
|
alias Pleroma.Web.StreamerView
|
||||||
require Pleroma.Constants
|
|
||||||
|
|
||||||
@mix_env Mix.env()
|
@mix_env Mix.env()
|
||||||
@registry Pleroma.Web.StreamerRegistry
|
@registry Pleroma.Web.StreamerRegistry
|
||||||
|
@ -253,17 +252,7 @@ defp do_stream("user", item) do
|
||||||
User.get_recipients_from_activity(item)
|
User.get_recipients_from_activity(item)
|
||||||
|> Enum.map(fn %{id: id} -> "user:#{id}" end)
|
|> Enum.map(fn %{id: id} -> "user:#{id}" end)
|
||||||
|
|
||||||
hashtag_recipients =
|
Enum.each(recipient_topics, fn topic ->
|
||||||
if Pleroma.Constants.as_public() in item.recipients do
|
|
||||||
Pleroma.Hashtag.get_recipients_for_activity(item)
|
|
||||||
|> Enum.map(fn id -> "user:#{id}" end)
|
|
||||||
else
|
|
||||||
[]
|
|
||||||
end
|
|
||||||
|
|
||||||
all_recipients = Enum.uniq(recipient_topics ++ hashtag_recipients)
|
|
||||||
|
|
||||||
Enum.each(all_recipients, fn topic ->
|
|
||||||
push_to_socket(topic, item)
|
push_to_socket(topic, item)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
defmodule Pleroma.Repo.Migrations.AddUserFollowsHashtag do
|
|
||||||
use Ecto.Migration
|
|
||||||
|
|
||||||
def change do
|
|
||||||
create table(:user_follows_hashtag) do
|
|
||||||
add(:hashtag_id, references(:hashtags))
|
|
||||||
add(:user_id, references(:users, type: :uuid, on_delete: :delete_all))
|
|
||||||
end
|
|
||||||
|
|
||||||
create(unique_index(:user_follows_hashtag, [:user_id, :hashtag_id]))
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -2679,74 +2679,4 @@ test "should report error on non-existing alias" do
|
||||||
assert user.ap_id in user3_updated.also_known_as
|
assert user.ap_id in user3_updated.also_known_as
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "follow_hashtag/2" do
|
|
||||||
test "should follow a hashtag" do
|
|
||||||
user = insert(:user)
|
|
||||||
hashtag = insert(:hashtag)
|
|
||||||
|
|
||||||
assert {:ok, _} = user |> User.follow_hashtag(hashtag)
|
|
||||||
|
|
||||||
user = User.get_cached_by_ap_id(user.ap_id)
|
|
||||||
|
|
||||||
assert user.followed_hashtags |> Enum.count() == 1
|
|
||||||
assert hashtag.name in Enum.map(user.followed_hashtags, fn %{name: name} -> name end)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "should not follow a hashtag twice" do
|
|
||||||
user = insert(:user)
|
|
||||||
hashtag = insert(:hashtag)
|
|
||||||
|
|
||||||
assert {:ok, _} = user |> User.follow_hashtag(hashtag)
|
|
||||||
|
|
||||||
assert {:ok, _} = user |> User.follow_hashtag(hashtag)
|
|
||||||
|
|
||||||
user = User.get_cached_by_ap_id(user.ap_id)
|
|
||||||
|
|
||||||
assert user.followed_hashtags |> Enum.count() == 1
|
|
||||||
assert hashtag.name in Enum.map(user.followed_hashtags, fn %{name: name} -> name end)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "can follow multiple hashtags" do
|
|
||||||
user = insert(:user)
|
|
||||||
hashtag = insert(:hashtag)
|
|
||||||
other_hashtag = insert(:hashtag)
|
|
||||||
|
|
||||||
assert {:ok, _} = user |> User.follow_hashtag(hashtag)
|
|
||||||
assert {:ok, _} = user |> User.follow_hashtag(other_hashtag)
|
|
||||||
|
|
||||||
user = User.get_cached_by_ap_id(user.ap_id)
|
|
||||||
|
|
||||||
assert user.followed_hashtags |> Enum.count() == 2
|
|
||||||
assert hashtag.name in Enum.map(user.followed_hashtags, fn %{name: name} -> name end)
|
|
||||||
assert other_hashtag.name in Enum.map(user.followed_hashtags, fn %{name: name} -> name end)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "unfollow_hashtag/2" do
|
|
||||||
test "should unfollow a hashtag" do
|
|
||||||
user = insert(:user)
|
|
||||||
hashtag = insert(:hashtag)
|
|
||||||
|
|
||||||
assert {:ok, _} = user |> User.follow_hashtag(hashtag)
|
|
||||||
assert {:ok, _} = user |> User.unfollow_hashtag(hashtag)
|
|
||||||
|
|
||||||
user = User.get_cached_by_ap_id(user.ap_id)
|
|
||||||
|
|
||||||
assert user.followed_hashtags |> Enum.count() == 0
|
|
||||||
end
|
|
||||||
|
|
||||||
test "should not error when trying to unfollow a hashtag twice" do
|
|
||||||
user = insert(:user)
|
|
||||||
hashtag = insert(:hashtag)
|
|
||||||
|
|
||||||
assert {:ok, _} = user |> User.follow_hashtag(hashtag)
|
|
||||||
assert {:ok, _} = user |> User.unfollow_hashtag(hashtag)
|
|
||||||
assert {:ok, _} = user |> User.unfollow_hashtag(hashtag)
|
|
||||||
|
|
||||||
user = User.get_cached_by_ap_id(user.ap_id)
|
|
||||||
|
|
||||||
assert user.followed_hashtags |> Enum.count() == 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -719,24 +719,6 @@ test "retrieve the activities for certain recipients" do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "fetch activities for followed hashtags" do
|
|
||||||
test "it should return public activities that reference a given hashtag" do
|
|
||||||
hashtag = insert(:hashtag, name: "tenshi")
|
|
||||||
user = insert(:user)
|
|
||||||
|
|
||||||
{:ok, public} = CommonAPI.post(user, %{status: "maji #tenshi", visibility: "public"})
|
|
||||||
{:ok, _unrelated} = CommonAPI.post(user, %{status: "dai #tensh", visibility: "public"})
|
|
||||||
{:ok, unlisted} = CommonAPI.post(user, %{status: "maji #tenshi", visibility: "unlisted"})
|
|
||||||
{:ok, _private} = CommonAPI.post(user, %{status: "maji #tenshi", visibility: "private"})
|
|
||||||
|
|
||||||
activities = ActivityPub.fetch_activities([], %{followed_hashtags: [hashtag.id]})
|
|
||||||
assert length(activities) == 2
|
|
||||||
public_id = public.id
|
|
||||||
unlisted_id = unlisted.id
|
|
||||||
assert [%{id: ^public_id}, %{id: ^unlisted_id}] = activities
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "fetch activities in context" do
|
describe "fetch activities in context" do
|
||||||
test "retrieves activities that have a given context" do
|
test "retrieves activities that have a given context" do
|
||||||
{:ok, activity} = ActivityBuilder.insert(%{"type" => "Create", "context" => "2hu"})
|
{:ok, activity} = ActivityBuilder.insert(%{"type" => "Create", "context" => "2hu"})
|
||||||
|
|
|
@ -1,97 +0,0 @@
|
||||||
defmodule Pleroma.Web.MastodonAPI.TagControllerTest do
|
|
||||||
use Pleroma.Web.ConnCase
|
|
||||||
|
|
||||||
import Pleroma.Factory
|
|
||||||
import Tesla.Mock
|
|
||||||
|
|
||||||
alias Pleroma.User
|
|
||||||
|
|
||||||
setup do
|
|
||||||
mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
|
||||||
:ok
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "GET /api/v1/tags/:id" do
|
|
||||||
test "returns 200 with tag" do
|
|
||||||
%{user: user, conn: conn} = oauth_access(["read"])
|
|
||||||
|
|
||||||
tag = insert(:hashtag, name: "jubjub")
|
|
||||||
{:ok, _user} = User.follow_hashtag(user, tag)
|
|
||||||
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> get("/api/v1/tags/jubjub")
|
|
||||||
|> json_response_and_validate_schema(200)
|
|
||||||
|
|
||||||
assert %{
|
|
||||||
"name" => "jubjub",
|
|
||||||
"url" => "http://localhost:4001/tags/jubjub",
|
|
||||||
"history" => [],
|
|
||||||
"following" => true
|
|
||||||
} = response
|
|
||||||
end
|
|
||||||
|
|
||||||
test "returns 404 with unknown tag" do
|
|
||||||
%{conn: conn} = oauth_access(["read"])
|
|
||||||
|
|
||||||
conn
|
|
||||||
|> get("/api/v1/tags/jubjub")
|
|
||||||
|> json_response_and_validate_schema(404)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "POST /api/v1/tags/:id/follow" do
|
|
||||||
test "should follow a hashtag" do
|
|
||||||
%{user: user, conn: conn} = oauth_access(["write:follows"])
|
|
||||||
hashtag = insert(:hashtag, name: "jubjub")
|
|
||||||
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> post("/api/v1/tags/jubjub/follow")
|
|
||||||
|> json_response_and_validate_schema(200)
|
|
||||||
|
|
||||||
assert response["following"] == true
|
|
||||||
user = User.get_cached_by_ap_id(user.ap_id)
|
|
||||||
assert User.following_hashtag?(user, hashtag)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "should 404 if hashtag doesn't exist" do
|
|
||||||
%{conn: conn} = oauth_access(["write:follows"])
|
|
||||||
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> post("/api/v1/tags/rubrub/follow")
|
|
||||||
|> json_response_and_validate_schema(404)
|
|
||||||
|
|
||||||
assert response["error"] == "Hashtag not found"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "POST /api/v1/tags/:id/unfollow" do
|
|
||||||
test "should unfollow a hashtag" do
|
|
||||||
%{user: user, conn: conn} = oauth_access(["write:follows"])
|
|
||||||
hashtag = insert(:hashtag, name: "jubjub")
|
|
||||||
{:ok, user} = User.follow_hashtag(user, hashtag)
|
|
||||||
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> post("/api/v1/tags/jubjub/unfollow")
|
|
||||||
|> json_response_and_validate_schema(200)
|
|
||||||
|
|
||||||
assert response["following"] == false
|
|
||||||
user = User.get_cached_by_ap_id(user.ap_id)
|
|
||||||
refute User.following_hashtag?(user, hashtag)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "should 404 if hashtag doesn't exist" do
|
|
||||||
%{conn: conn} = oauth_access(["write:follows"])
|
|
||||||
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> post("/api/v1/tags/rubrub/unfollow")
|
|
||||||
|> json_response_and_validate_schema(404)
|
|
||||||
|
|
||||||
assert response["error"] == "Hashtag not found"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -410,36 +410,6 @@ test "it streams own edits in the 'user' stream", %{user: user, token: oauth_tok
|
||||||
assert_receive {:render_with_user, _, "status_update.json", ^create, ^stream}
|
assert_receive {:render_with_user, _, "status_update.json", ^create, ^stream}
|
||||||
refute Streamer.filtered_by_user?(user, edited)
|
refute Streamer.filtered_by_user?(user, edited)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it streams posts containing followed hashtags on the 'user' stream", %{
|
|
||||||
user: user,
|
|
||||||
token: oauth_token
|
|
||||||
} do
|
|
||||||
hashtag = insert(:hashtag, %{name: "tenshi"})
|
|
||||||
other_user = insert(:user)
|
|
||||||
{:ok, user} = User.follow_hashtag(user, hashtag)
|
|
||||||
|
|
||||||
Streamer.get_topic_and_add_socket("user", user, oauth_token)
|
|
||||||
{:ok, activity} = CommonAPI.post(other_user, %{status: "hey #tenshi"})
|
|
||||||
|
|
||||||
assert_receive {:render_with_user, _, "update.json", ^activity, _}
|
|
||||||
end
|
|
||||||
|
|
||||||
test "should not stream private posts containing followed hashtags on the 'user' stream", %{
|
|
||||||
user: user,
|
|
||||||
token: oauth_token
|
|
||||||
} do
|
|
||||||
hashtag = insert(:hashtag, %{name: "tenshi"})
|
|
||||||
other_user = insert(:user)
|
|
||||||
{:ok, user} = User.follow_hashtag(user, hashtag)
|
|
||||||
|
|
||||||
Streamer.get_topic_and_add_socket("user", user, oauth_token)
|
|
||||||
|
|
||||||
{:ok, activity} =
|
|
||||||
CommonAPI.post(other_user, %{status: "hey #tenshi", visibility: "private"})
|
|
||||||
|
|
||||||
refute_receive {:render_with_user, _, "update.json", ^activity, _}
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "public streams" do
|
describe "public streams" do
|
||||||
|
|
|
@ -716,11 +716,4 @@ def delivery_factory(params \\ %{}) do
|
||||||
user: user
|
user: user
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def hashtag_factory(params \\ %{}) do
|
|
||||||
%Pleroma.Hashtag{
|
|
||||||
name: "test #{sequence(:hashtag_name, & &1)}"
|
|
||||||
}
|
|
||||||
|> Map.merge(params)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue