Feature/1072 muting notifications
This commit is contained in:
parent
9f211838ec
commit
e7c39b7ac8
|
@ -27,13 +27,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Mastodon API: Support for the [`tagged` filter](https://github.com/tootsuite/mastodon/pull/9755) in [`GET /api/v1/accounts/:id/statuses`](https://docs.joinmastodon.org/api/rest/accounts/#get-api-v1-accounts-id-statuses)
|
- Mastodon API: Support for the [`tagged` filter](https://github.com/tootsuite/mastodon/pull/9755) in [`GET /api/v1/accounts/:id/statuses`](https://docs.joinmastodon.org/api/rest/accounts/#get-api-v1-accounts-id-statuses)
|
||||||
- Mastodon API, streaming: Add support for passing the token in the `Sec-WebSocket-Protocol` header
|
- Mastodon API, streaming: Add support for passing the token in the `Sec-WebSocket-Protocol` header
|
||||||
- Mastodon API, extension: Ability to reset avatar, profile banner, and background
|
- Mastodon API, extension: Ability to reset avatar, profile banner, and background
|
||||||
|
- Mastodon API: Add support for categories for custom emojis by reusing the group feature. <https://github.com/tootsuite/mastodon/pull/11196>
|
||||||
|
- Mastodon API: Add support for muting/unmuting notifications
|
||||||
- Admin API: Return users' tags when querying reports
|
- Admin API: Return users' tags when querying reports
|
||||||
- Admin API: Return avatar and display name when querying users
|
- Admin API: Return avatar and display name when querying users
|
||||||
- Admin API: Allow querying user by ID
|
- Admin API: Allow querying user by ID
|
||||||
- Admin API: Added support for `tuples`.
|
- Admin API: Added support for `tuples`.
|
||||||
- Added synchronization of following/followers counters for external users
|
- Added synchronization of following/followers counters for external users
|
||||||
- Configuration: `enabled` option for `Pleroma.Emails.Mailer`, defaulting to `false`.
|
- Configuration: `enabled` option for `Pleroma.Emails.Mailer`, defaulting to `false`.
|
||||||
- Mastodon API: Add support for categories for custom emojis by reusing the group feature. <https://github.com/tootsuite/mastodon/pull/11196>
|
|
||||||
- Configuration: Pleroma.Plugs.RateLimiter `bucket_name`, `params` options.
|
- Configuration: Pleroma.Plugs.RateLimiter `bucket_name`, `params` options.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
|
@ -11,7 +11,6 @@ defmodule Pleroma.Notification do
|
||||||
alias Pleroma.Pagination
|
alias Pleroma.Pagination
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.CommonAPI
|
|
||||||
alias Pleroma.Web.CommonAPI.Utils
|
alias Pleroma.Web.CommonAPI.Utils
|
||||||
alias Pleroma.Web.Push
|
alias Pleroma.Web.Push
|
||||||
alias Pleroma.Web.Streamer
|
alias Pleroma.Web.Streamer
|
||||||
|
@ -32,31 +31,47 @@ def changeset(%Notification{} = notification, attrs) do
|
||||||
|> cast(attrs, [:seen])
|
|> cast(attrs, [:seen])
|
||||||
end
|
end
|
||||||
|
|
||||||
def for_user_query(user) do
|
def for_user_query(user, opts) do
|
||||||
Notification
|
query =
|
||||||
|> where(user_id: ^user.id)
|
Notification
|
||||||
|> where(
|
|> where(user_id: ^user.id)
|
||||||
[n, a],
|
|> where(
|
||||||
fragment(
|
[n, a],
|
||||||
"? not in (SELECT ap_id FROM users WHERE info->'deactivated' @> 'true')",
|
|
||||||
a.actor
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|> join(:inner, [n], activity in assoc(n, :activity))
|
|
||||||
|> join(:left, [n, a], object in Object,
|
|
||||||
on:
|
|
||||||
fragment(
|
fragment(
|
||||||
"(?->>'id') = COALESCE((? -> 'object'::text) ->> 'id'::text)",
|
"? not in (SELECT ap_id FROM users WHERE info->'deactivated' @> 'true')",
|
||||||
object.data,
|
a.actor
|
||||||
a.data
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|> preload([n, a, o], activity: {a, object: o})
|
|> join(:inner, [n], activity in assoc(n, :activity))
|
||||||
|
|> join(:left, [n, a], object in Object,
|
||||||
|
on:
|
||||||
|
fragment(
|
||||||
|
"(?->>'id') = COALESCE((? -> 'object'::text) ->> 'id'::text)",
|
||||||
|
object.data,
|
||||||
|
a.data
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|> preload([n, a, o], activity: {a, object: o})
|
||||||
|
|
||||||
|
if opts[:with_muted] do
|
||||||
|
query
|
||||||
|
else
|
||||||
|
where(query, [n, a], a.actor not in ^user.info.muted_notifications)
|
||||||
|
|> where([n, a], a.actor not in ^user.info.blocks)
|
||||||
|
|> where(
|
||||||
|
[n, a],
|
||||||
|
fragment("substring(? from '.*://([^/]*)')", a.actor) not in ^user.info.domain_blocks
|
||||||
|
)
|
||||||
|
|> join(:left, [n, a], tm in Pleroma.ThreadMute,
|
||||||
|
on: tm.user_id == ^user.id and tm.context == fragment("?->>'context'", a.data)
|
||||||
|
)
|
||||||
|
|> where([n, a, o, tm], is_nil(tm.id))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def for_user(user, opts \\ %{}) do
|
def for_user(user, opts \\ %{}) do
|
||||||
user
|
user
|
||||||
|> for_user_query()
|
|> for_user_query(opts)
|
||||||
|> Pagination.fetch_paginated(opts)
|
|> Pagination.fetch_paginated(opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -179,11 +194,10 @@ def get_notified_from_activity(
|
||||||
|
|
||||||
def get_notified_from_activity(_, _local_only), do: []
|
def get_notified_from_activity(_, _local_only), do: []
|
||||||
|
|
||||||
|
@spec skip?(Activity.t(), User.t()) :: boolean()
|
||||||
def skip?(activity, user) do
|
def skip?(activity, user) do
|
||||||
[
|
[
|
||||||
:self,
|
:self,
|
||||||
:blocked,
|
|
||||||
:muted,
|
|
||||||
:followers,
|
:followers,
|
||||||
:follows,
|
:follows,
|
||||||
:non_followers,
|
:non_followers,
|
||||||
|
@ -193,21 +207,11 @@ def skip?(activity, user) do
|
||||||
|> Enum.any?(&skip?(&1, activity, user))
|
|> Enum.any?(&skip?(&1, activity, user))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec skip?(atom(), Activity.t(), User.t()) :: boolean()
|
||||||
def skip?(:self, activity, user) do
|
def skip?(:self, activity, user) do
|
||||||
activity.data["actor"] == user.ap_id
|
activity.data["actor"] == user.ap_id
|
||||||
end
|
end
|
||||||
|
|
||||||
def skip?(:blocked, activity, user) do
|
|
||||||
actor = activity.data["actor"]
|
|
||||||
User.blocks?(user, %{ap_id: actor})
|
|
||||||
end
|
|
||||||
|
|
||||||
def skip?(:muted, activity, user) do
|
|
||||||
actor = activity.data["actor"]
|
|
||||||
|
|
||||||
User.mutes?(user, %{ap_id: actor}) or CommonAPI.thread_muted?(user, activity)
|
|
||||||
end
|
|
||||||
|
|
||||||
def skip?(
|
def skip?(
|
||||||
:followers,
|
:followers,
|
||||||
activity,
|
activity,
|
||||||
|
|
|
@ -749,10 +749,13 @@ def get_recipients_from_activity(%Activity{recipients: to}) do
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
end
|
end
|
||||||
|
|
||||||
def mute(muter, %User{ap_id: ap_id}) do
|
@spec mute(User.t(), User.t(), boolean()) :: {:ok, User.t()} | {:error, String.t()}
|
||||||
|
def mute(muter, %User{ap_id: ap_id}, notifications? \\ true) do
|
||||||
|
info = muter.info
|
||||||
|
|
||||||
info_cng =
|
info_cng =
|
||||||
muter.info
|
User.Info.add_to_mutes(info, ap_id)
|
||||||
|> User.Info.add_to_mutes(ap_id)
|
|> User.Info.add_to_muted_notifications(info, ap_id, notifications?)
|
||||||
|
|
||||||
cng =
|
cng =
|
||||||
change(muter)
|
change(muter)
|
||||||
|
@ -762,9 +765,11 @@ def mute(muter, %User{ap_id: ap_id}) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def unmute(muter, %{ap_id: ap_id}) do
|
def unmute(muter, %{ap_id: ap_id}) do
|
||||||
|
info = muter.info
|
||||||
|
|
||||||
info_cng =
|
info_cng =
|
||||||
muter.info
|
User.Info.remove_from_mutes(info, ap_id)
|
||||||
|> User.Info.remove_from_mutes(ap_id)
|
|> User.Info.remove_from_muted_notifications(info, ap_id)
|
||||||
|
|
||||||
cng =
|
cng =
|
||||||
change(muter)
|
change(muter)
|
||||||
|
@ -860,6 +865,12 @@ def unblock(blocker, %{ap_id: ap_id}) do
|
||||||
def mutes?(nil, _), do: false
|
def mutes?(nil, _), do: false
|
||||||
def mutes?(user, %{ap_id: ap_id}), do: Enum.member?(user.info.mutes, ap_id)
|
def mutes?(user, %{ap_id: ap_id}), do: Enum.member?(user.info.mutes, ap_id)
|
||||||
|
|
||||||
|
@spec muted_notifications?(User.t() | nil, User.t() | map()) :: boolean()
|
||||||
|
def muted_notifications?(nil, _), do: false
|
||||||
|
|
||||||
|
def muted_notifications?(user, %{ap_id: ap_id}),
|
||||||
|
do: Enum.member?(user.info.muted_notifications, ap_id)
|
||||||
|
|
||||||
def blocks?(%User{info: info} = _user, %{ap_id: ap_id}) do
|
def blocks?(%User{info: info} = _user, %{ap_id: ap_id}) do
|
||||||
blocks = info.blocks
|
blocks = info.blocks
|
||||||
domain_blocks = info.domain_blocks
|
domain_blocks = info.domain_blocks
|
||||||
|
|
|
@ -24,6 +24,7 @@ defmodule Pleroma.User.Info do
|
||||||
field(:domain_blocks, {:array, :string}, default: [])
|
field(:domain_blocks, {:array, :string}, default: [])
|
||||||
field(:mutes, {:array, :string}, default: [])
|
field(:mutes, {:array, :string}, default: [])
|
||||||
field(:muted_reblogs, {:array, :string}, default: [])
|
field(:muted_reblogs, {:array, :string}, default: [])
|
||||||
|
field(:muted_notifications, {:array, :string}, default: [])
|
||||||
field(:subscribers, {:array, :string}, default: [])
|
field(:subscribers, {:array, :string}, default: [])
|
||||||
field(:deactivated, :boolean, default: false)
|
field(:deactivated, :boolean, default: false)
|
||||||
field(:no_rich_text, :boolean, default: false)
|
field(:no_rich_text, :boolean, default: false)
|
||||||
|
@ -120,6 +121,16 @@ def set_mutes(info, mutes) do
|
||||||
|> validate_required([:mutes])
|
|> validate_required([:mutes])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec set_notification_mutes(Changeset.t(), [String.t()], boolean()) :: Changeset.t()
|
||||||
|
def set_notification_mutes(changeset, muted_notifications, notifications?) do
|
||||||
|
if notifications? do
|
||||||
|
put_change(changeset, :muted_notifications, muted_notifications)
|
||||||
|
|> validate_required([:muted_notifications])
|
||||||
|
else
|
||||||
|
changeset
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def set_blocks(info, blocks) do
|
def set_blocks(info, blocks) do
|
||||||
params = %{blocks: blocks}
|
params = %{blocks: blocks}
|
||||||
|
|
||||||
|
@ -136,14 +147,31 @@ def set_subscribers(info, subscribers) do
|
||||||
|> validate_required([:subscribers])
|
|> validate_required([:subscribers])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec add_to_mutes(Info.t(), String.t()) :: Changeset.t()
|
||||||
def add_to_mutes(info, muted) do
|
def add_to_mutes(info, muted) do
|
||||||
set_mutes(info, Enum.uniq([muted | info.mutes]))
|
set_mutes(info, Enum.uniq([muted | info.mutes]))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec add_to_muted_notifications(Changeset.t(), Info.t(), String.t(), boolean()) ::
|
||||||
|
Changeset.t()
|
||||||
|
def add_to_muted_notifications(changeset, info, muted, notifications?) do
|
||||||
|
set_notification_mutes(
|
||||||
|
changeset,
|
||||||
|
Enum.uniq([muted | info.muted_notifications]),
|
||||||
|
notifications?
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec remove_from_mutes(Info.t(), String.t()) :: Changeset.t()
|
||||||
def remove_from_mutes(info, muted) do
|
def remove_from_mutes(info, muted) do
|
||||||
set_mutes(info, List.delete(info.mutes, muted))
|
set_mutes(info, List.delete(info.mutes, muted))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec remove_from_muted_notifications(Changeset.t(), Info.t(), String.t()) :: Changeset.t()
|
||||||
|
def remove_from_muted_notifications(changeset, info, muted) do
|
||||||
|
set_notification_mutes(changeset, List.delete(info.muted_notifications, muted), true)
|
||||||
|
end
|
||||||
|
|
||||||
def add_to_block(info, blocked) do
|
def add_to_block(info, blocked) do
|
||||||
set_blocks(info, Enum.uniq([blocked | info.blocks]))
|
set_blocks(info, Enum.uniq([blocked | info.blocks]))
|
||||||
end
|
end
|
||||||
|
|
|
@ -53,7 +53,7 @@ def get_notifications(user, params \\ %{}) do
|
||||||
options = cast_params(params)
|
options = cast_params(params)
|
||||||
|
|
||||||
user
|
user
|
||||||
|> Notification.for_user_query()
|
|> Notification.for_user_query(options)
|
||||||
|> restrict(:exclude_types, options)
|
|> restrict(:exclude_types, options)
|
||||||
|> Pagination.fetch_paginated(params)
|
|> Pagination.fetch_paginated(params)
|
||||||
end
|
end
|
||||||
|
@ -67,7 +67,8 @@ def get_scheduled_activities(user, params \\ %{}) do
|
||||||
defp cast_params(params) do
|
defp cast_params(params) do
|
||||||
param_types = %{
|
param_types = %{
|
||||||
exclude_types: {:array, :string},
|
exclude_types: {:array, :string},
|
||||||
reblogs: :boolean
|
reblogs: :boolean,
|
||||||
|
with_muted: :boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
changeset = cast({%{}, param_types}, params, Map.keys(param_types))
|
changeset = cast({%{}, param_types}, params, Map.keys(param_types))
|
||||||
|
|
|
@ -1068,9 +1068,14 @@ def unfollow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def mute(%{assigns: %{user: muter}} = conn, %{"id" => id}) do
|
def mute(%{assigns: %{user: muter}} = conn, %{"id" => id} = params) do
|
||||||
|
notifications =
|
||||||
|
if Map.has_key?(params, "notifications"),
|
||||||
|
do: params["notifications"] in [true, "True", "true", "1"],
|
||||||
|
else: true
|
||||||
|
|
||||||
with %User{} = muted <- User.get_cached_by_id(id),
|
with %User{} = muted <- User.get_cached_by_id(id),
|
||||||
{:ok, muter} <- User.mute(muter, muted) do
|
{:ok, muter} <- User.mute(muter, muted, notifications) do
|
||||||
conn
|
conn
|
||||||
|> put_view(AccountView)
|
|> put_view(AccountView)
|
||||||
|> render("relationship.json", %{user: muter, target: muted})
|
|> render("relationship.json", %{user: muter, target: muted})
|
||||||
|
|
|
@ -52,7 +52,7 @@ def render("relationship.json", %{user: %User{} = user, target: %User{} = target
|
||||||
followed_by: User.following?(target, user),
|
followed_by: User.following?(target, user),
|
||||||
blocking: User.blocks?(user, target),
|
blocking: User.blocks?(user, target),
|
||||||
muting: User.mutes?(user, target),
|
muting: User.mutes?(user, target),
|
||||||
muting_notifications: false,
|
muting_notifications: User.muted_notifications?(user, target),
|
||||||
subscribing: User.subscribed_to?(user, target),
|
subscribing: User.subscribed_to?(user, target),
|
||||||
requested: requested,
|
requested: requested,
|
||||||
domain_blocking: false,
|
domain_blocking: false,
|
||||||
|
|
|
@ -192,6 +192,13 @@ def dm_timeline(%{assigns: %{user: user}} = conn, params) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def notifications(%{assigns: %{user: user}} = conn, params) do
|
def notifications(%{assigns: %{user: user}} = conn, params) do
|
||||||
|
params =
|
||||||
|
if Map.has_key?(params, "with_muted") do
|
||||||
|
Map.put(params, :with_muted, params["with_muted"] in [true, "True", "true", "1"])
|
||||||
|
else
|
||||||
|
params
|
||||||
|
end
|
||||||
|
|
||||||
notifications = Notification.for_user(user, params)
|
notifications = Notification.for_user(user, params)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.CopyMutedToMutedNotifications do
|
||||||
|
use Ecto.Migration
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
|
def change do
|
||||||
|
query =
|
||||||
|
User.Query.build(%{
|
||||||
|
local: true,
|
||||||
|
active: true,
|
||||||
|
order_by: :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)
|
||||||
|
end
|
||||||
|
end
|
|
@ -74,26 +74,37 @@ test "it creates a notification for user and send to the 'user' and the 'user:no
|
||||||
Task.await(task_user_notification)
|
Task.await(task_user_notification)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it doesn't create a notification for user if the user blocks the activity author" do
|
test "it creates a notification for user if the user blocks the activity author" do
|
||||||
activity = insert(:note_activity)
|
activity = insert(:note_activity)
|
||||||
author = User.get_cached_by_ap_id(activity.data["actor"])
|
author = User.get_cached_by_ap_id(activity.data["actor"])
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
{:ok, user} = User.block(user, author)
|
{:ok, user} = User.block(user, author)
|
||||||
|
|
||||||
refute Notification.create_notification(activity, user)
|
assert Notification.create_notification(activity, user)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it doesn't create a notificatin for the user if the user mutes the activity author" do
|
test "it creates a notificatin for the user if the user mutes the activity author" do
|
||||||
muter = insert(:user)
|
muter = insert(:user)
|
||||||
muted = insert(:user)
|
muted = insert(:user)
|
||||||
{:ok, _} = User.mute(muter, muted)
|
{:ok, _} = User.mute(muter, muted)
|
||||||
muter = Repo.get(User, muter.id)
|
muter = Repo.get(User, muter.id)
|
||||||
{:ok, activity} = CommonAPI.post(muted, %{"status" => "Hi @#{muter.nickname}"})
|
{:ok, activity} = CommonAPI.post(muted, %{"status" => "Hi @#{muter.nickname}"})
|
||||||
|
|
||||||
refute Notification.create_notification(activity, muter)
|
assert Notification.create_notification(activity, muter)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it doesn't create a notification for an activity from a muted thread" do
|
test "notification created if user is muted without notifications" do
|
||||||
|
muter = insert(:user)
|
||||||
|
muted = insert(:user)
|
||||||
|
|
||||||
|
{:ok, muter} = User.mute(muter, muted, false)
|
||||||
|
|
||||||
|
{:ok, activity} = CommonAPI.post(muted, %{"status" => "Hi @#{muter.nickname}"})
|
||||||
|
|
||||||
|
assert Notification.create_notification(activity, muter)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it creates a notification for an activity from a muted thread" do
|
||||||
muter = insert(:user)
|
muter = insert(:user)
|
||||||
other_user = insert(:user)
|
other_user = insert(:user)
|
||||||
{:ok, activity} = CommonAPI.post(muter, %{"status" => "hey"})
|
{:ok, activity} = CommonAPI.post(muter, %{"status" => "hey"})
|
||||||
|
@ -105,7 +116,7 @@ test "it doesn't create a notification for an activity from a muted thread" do
|
||||||
"in_reply_to_status_id" => activity.id
|
"in_reply_to_status_id" => activity.id
|
||||||
})
|
})
|
||||||
|
|
||||||
refute Notification.create_notification(activity, muter)
|
assert Notification.create_notification(activity, muter)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it disables notifications from followers" do
|
test "it disables notifications from followers" do
|
||||||
|
@ -532,4 +543,98 @@ test "replying to a deleted post without tagging does not generate a notificatio
|
||||||
assert Enum.empty?(Notification.for_user(user))
|
assert Enum.empty?(Notification.for_user(user))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "for_user" do
|
||||||
|
test "it returns notifications for muted user without notifications" do
|
||||||
|
user = insert(:user)
|
||||||
|
muted = insert(:user)
|
||||||
|
{:ok, user} = User.mute(user, muted, false)
|
||||||
|
|
||||||
|
{:ok, _activity} = TwitterAPI.create_status(muted, %{"status" => "hey @#{user.nickname}"})
|
||||||
|
|
||||||
|
assert length(Notification.for_user(user)) == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it doesn't return notifications for muted user with notifications" do
|
||||||
|
user = insert(:user)
|
||||||
|
muted = insert(:user)
|
||||||
|
{:ok, user} = User.mute(user, muted)
|
||||||
|
|
||||||
|
{:ok, _activity} = TwitterAPI.create_status(muted, %{"status" => "hey @#{user.nickname}"})
|
||||||
|
|
||||||
|
assert Notification.for_user(user) == []
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it doesn't return notifications for blocked user" do
|
||||||
|
user = insert(:user)
|
||||||
|
blocked = insert(:user)
|
||||||
|
{:ok, user} = User.block(user, blocked)
|
||||||
|
|
||||||
|
{:ok, _activity} = TwitterAPI.create_status(blocked, %{"status" => "hey @#{user.nickname}"})
|
||||||
|
|
||||||
|
assert Notification.for_user(user) == []
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it doesn't return notificatitons for blocked domain" do
|
||||||
|
user = insert(:user)
|
||||||
|
blocked = insert(:user, ap_id: "http://some-domain.com")
|
||||||
|
{:ok, user} = User.block_domain(user, "some-domain.com")
|
||||||
|
|
||||||
|
{:ok, _activity} = TwitterAPI.create_status(blocked, %{"status" => "hey @#{user.nickname}"})
|
||||||
|
|
||||||
|
assert Notification.for_user(user) == []
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it doesn't return notifications for muted thread" do
|
||||||
|
user = insert(:user)
|
||||||
|
another_user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} =
|
||||||
|
TwitterAPI.create_status(another_user, %{"status" => "hey @#{user.nickname}"})
|
||||||
|
|
||||||
|
{:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"])
|
||||||
|
assert Notification.for_user(user) == []
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns notifications for muted user with notifications and with_muted parameter" do
|
||||||
|
user = insert(:user)
|
||||||
|
muted = insert(:user)
|
||||||
|
{:ok, user} = User.mute(user, muted)
|
||||||
|
|
||||||
|
{:ok, _activity} = TwitterAPI.create_status(muted, %{"status" => "hey @#{user.nickname}"})
|
||||||
|
|
||||||
|
assert length(Notification.for_user(user, %{with_muted: true})) == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns notifications for blocked user and with_muted parameter" do
|
||||||
|
user = insert(:user)
|
||||||
|
blocked = insert(:user)
|
||||||
|
{:ok, user} = User.block(user, blocked)
|
||||||
|
|
||||||
|
{:ok, _activity} = TwitterAPI.create_status(blocked, %{"status" => "hey @#{user.nickname}"})
|
||||||
|
|
||||||
|
assert length(Notification.for_user(user, %{with_muted: true})) == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns notificatitons for blocked domain and with_muted parameter" do
|
||||||
|
user = insert(:user)
|
||||||
|
blocked = insert(:user, ap_id: "http://some-domain.com")
|
||||||
|
{:ok, user} = User.block_domain(user, "some-domain.com")
|
||||||
|
|
||||||
|
{:ok, _activity} = TwitterAPI.create_status(blocked, %{"status" => "hey @#{user.nickname}"})
|
||||||
|
|
||||||
|
assert length(Notification.for_user(user, %{with_muted: true})) == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns notifications for muted thread with_muted parameter" do
|
||||||
|
user = insert(:user)
|
||||||
|
another_user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} =
|
||||||
|
TwitterAPI.create_status(another_user, %{"status" => "hey @#{user.nickname}"})
|
||||||
|
|
||||||
|
{:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"])
|
||||||
|
assert length(Notification.for_user(user, %{with_muted: true})) == 1
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -687,10 +687,12 @@ test "it mutes people" do
|
||||||
muted_user = insert(:user)
|
muted_user = insert(:user)
|
||||||
|
|
||||||
refute User.mutes?(user, muted_user)
|
refute User.mutes?(user, muted_user)
|
||||||
|
refute User.muted_notifications?(user, muted_user)
|
||||||
|
|
||||||
{:ok, user} = User.mute(user, muted_user)
|
{:ok, user} = User.mute(user, muted_user)
|
||||||
|
|
||||||
assert User.mutes?(user, muted_user)
|
assert User.mutes?(user, muted_user)
|
||||||
|
assert User.muted_notifications?(user, muted_user)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it unmutes users" do
|
test "it unmutes users" do
|
||||||
|
@ -701,6 +703,20 @@ test "it unmutes users" do
|
||||||
{:ok, user} = User.unmute(user, muted_user)
|
{:ok, user} = User.unmute(user, muted_user)
|
||||||
|
|
||||||
refute User.mutes?(user, muted_user)
|
refute User.mutes?(user, muted_user)
|
||||||
|
refute User.muted_notifications?(user, muted_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it mutes user without notifications" do
|
||||||
|
user = insert(:user)
|
||||||
|
muted_user = insert(:user)
|
||||||
|
|
||||||
|
refute User.mutes?(user, muted_user)
|
||||||
|
refute User.muted_notifications?(user, muted_user)
|
||||||
|
|
||||||
|
{:ok, user} = User.mute(user, muted_user, false)
|
||||||
|
|
||||||
|
assert User.mutes?(user, muted_user)
|
||||||
|
refute User.muted_notifications?(user, muted_user)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1274,6 +1274,71 @@ test "destroy multiple", %{conn: conn} do
|
||||||
result = json_response(conn_res, 200)
|
result = json_response(conn_res, 200)
|
||||||
assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
|
assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "doesn't see notifications after muting user with notifications", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
user2 = insert(:user)
|
||||||
|
|
||||||
|
{:ok, _, _, _} = CommonAPI.follow(user, user2)
|
||||||
|
{:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
|
||||||
|
|
||||||
|
conn = assign(conn, :user, user)
|
||||||
|
|
||||||
|
conn = get(conn, "/api/v1/notifications")
|
||||||
|
|
||||||
|
assert length(json_response(conn, 200)) == 1
|
||||||
|
|
||||||
|
{:ok, user} = User.mute(user, user2)
|
||||||
|
|
||||||
|
conn = assign(build_conn(), :user, user)
|
||||||
|
conn = get(conn, "/api/v1/notifications")
|
||||||
|
|
||||||
|
assert json_response(conn, 200) == []
|
||||||
|
end
|
||||||
|
|
||||||
|
test "see notifications after muting user without notifications", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
user2 = insert(:user)
|
||||||
|
|
||||||
|
{:ok, _, _, _} = CommonAPI.follow(user, user2)
|
||||||
|
{:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
|
||||||
|
|
||||||
|
conn = assign(conn, :user, user)
|
||||||
|
|
||||||
|
conn = get(conn, "/api/v1/notifications")
|
||||||
|
|
||||||
|
assert length(json_response(conn, 200)) == 1
|
||||||
|
|
||||||
|
{:ok, user} = User.mute(user, user2, false)
|
||||||
|
|
||||||
|
conn = assign(build_conn(), :user, user)
|
||||||
|
conn = get(conn, "/api/v1/notifications")
|
||||||
|
|
||||||
|
assert length(json_response(conn, 200)) == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
test "see notifications after muting user with notifications and with_muted parameter", %{
|
||||||
|
conn: conn
|
||||||
|
} do
|
||||||
|
user = insert(:user)
|
||||||
|
user2 = insert(:user)
|
||||||
|
|
||||||
|
{:ok, _, _, _} = CommonAPI.follow(user, user2)
|
||||||
|
{:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
|
||||||
|
|
||||||
|
conn = assign(conn, :user, user)
|
||||||
|
|
||||||
|
conn = get(conn, "/api/v1/notifications")
|
||||||
|
|
||||||
|
assert length(json_response(conn, 200)) == 1
|
||||||
|
|
||||||
|
{:ok, user} = User.mute(user, user2)
|
||||||
|
|
||||||
|
conn = assign(build_conn(), :user, user)
|
||||||
|
conn = get(conn, "/api/v1/notifications", %{"with_muted" => "true"})
|
||||||
|
|
||||||
|
assert length(json_response(conn, 200)) == 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "reblogging" do
|
describe "reblogging" do
|
||||||
|
@ -2105,25 +2170,52 @@ test "following / unfollowing errors" do
|
||||||
assert %{"error" => "Record not found"} = json_response(conn_res, 404)
|
assert %{"error" => "Record not found"} = json_response(conn_res, 404)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "muting / unmuting a user", %{conn: conn} do
|
describe "mute/unmute" do
|
||||||
user = insert(:user)
|
test "with notifications", %{conn: conn} do
|
||||||
other_user = insert(:user)
|
user = insert(:user)
|
||||||
|
other_user = insert(:user)
|
||||||
|
|
||||||
conn =
|
conn =
|
||||||
conn
|
conn
|
||||||
|> assign(:user, user)
|
|> assign(:user, user)
|
||||||
|> post("/api/v1/accounts/#{other_user.id}/mute")
|
|> post("/api/v1/accounts/#{other_user.id}/mute")
|
||||||
|
|
||||||
assert %{"id" => _id, "muting" => true} = json_response(conn, 200)
|
response = json_response(conn, 200)
|
||||||
|
|
||||||
user = User.get_cached_by_id(user.id)
|
assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
|
||||||
|
user = User.get_cached_by_id(user.id)
|
||||||
|
|
||||||
conn =
|
conn =
|
||||||
build_conn()
|
build_conn()
|
||||||
|> assign(:user, user)
|
|> assign(:user, user)
|
||||||
|> post("/api/v1/accounts/#{other_user.id}/unmute")
|
|> post("/api/v1/accounts/#{other_user.id}/unmute")
|
||||||
|
|
||||||
assert %{"id" => _id, "muting" => false} = json_response(conn, 200)
|
response = json_response(conn, 200)
|
||||||
|
assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
|
||||||
|
end
|
||||||
|
|
||||||
|
test "without notifications", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
other_user = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
|
||||||
|
|
||||||
|
response = json_response(conn, 200)
|
||||||
|
|
||||||
|
assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
|
||||||
|
user = User.get_cached_by_id(user.id)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> post("/api/v1/accounts/#{other_user.id}/unmute")
|
||||||
|
|
||||||
|
response = json_response(conn, 200)
|
||||||
|
assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "subscribing / unsubscribing to a user", %{conn: conn} do
|
test "subscribing / unsubscribing to a user", %{conn: conn} do
|
||||||
|
|
|
@ -521,6 +521,38 @@ test "with credentials", %{conn: conn, user: current_user} do
|
||||||
for: current_user
|
for: current_user
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "muted user", %{conn: conn, user: current_user} do
|
||||||
|
other_user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, current_user} = User.mute(current_user, other_user)
|
||||||
|
|
||||||
|
{:ok, _activity} =
|
||||||
|
ActivityBuilder.insert(%{"to" => [current_user.ap_id]}, %{user: other_user})
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> with_credentials(current_user.nickname, "test")
|
||||||
|
|> get("/api/qvitter/statuses/notifications.json")
|
||||||
|
|
||||||
|
assert json_response(conn, 200) == []
|
||||||
|
end
|
||||||
|
|
||||||
|
test "muted user with with_muted parameter", %{conn: conn, user: current_user} do
|
||||||
|
other_user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, current_user} = User.mute(current_user, other_user)
|
||||||
|
|
||||||
|
{:ok, _activity} =
|
||||||
|
ActivityBuilder.insert(%{"to" => [current_user.ap_id]}, %{user: other_user})
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> with_credentials(current_user.nickname, "test")
|
||||||
|
|> get("/api/qvitter/statuses/notifications.json", %{"with_muted" => "true"})
|
||||||
|
|
||||||
|
assert length(json_response(conn, 200)) == 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "POST /api/qvitter/statuses/notifications/read" do
|
describe "POST /api/qvitter/statuses/notifications/read" do
|
||||||
|
|
Loading…
Reference in a new issue