diff --git a/app/controllers/api/v1/statuses_controller.rb b/app/controllers/api/v1/statuses_controller.rb
index 77bdaa494..9312282ed 100644
--- a/app/controllers/api/v1/statuses_controller.rb
+++ b/app/controllers/api/v1/statuses_controller.rb
@@ -1,10 +1,11 @@
# frozen_string_literal: true
class Api::V1::StatusesController < ApiController
- before_action :authorize_if_got_token, except: [:create, :destroy, :reblog, :unreblog, :favourite, :unfavourite]
- before_action -> { doorkeeper_authorize! :write }, only: [:create, :destroy, :reblog, :unreblog, :favourite, :unfavourite]
- before_action :require_user!, except: [:show, :context, :card, :reblogged_by, :favourited_by]
- before_action :set_status, only: [:show, :context, :card, :reblogged_by, :favourited_by]
+ before_action :authorize_if_got_token, except: [:create, :destroy, :reblog, :unreblog, :favourite, :unfavourite, :mute, :unmute]
+ before_action -> { doorkeeper_authorize! :write }, only: [:create, :destroy, :reblog, :unreblog, :favourite, :unfavourite, :mute, :unmute]
+ before_action :require_user!, except: [:show, :context, :card, :reblogged_by, :favourited_by]
+ before_action :set_status, only: [:show, :context, :card, :reblogged_by, :favourited_by, :mute, :unmute]
+ before_action :set_conversation, only: [:mute, :unmute]
respond_to :json
@@ -105,6 +106,22 @@ class Api::V1::StatusesController < ApiController
render :show
end
+ def mute
+ current_account.mute_conversation!(@conversation)
+
+ @mutes_map = { @conversation.id => true }
+
+ render :show
+ end
+
+ def unmute
+ current_account.unmute_conversation!(@conversation)
+
+ @mutes_map = { @conversation.id => false }
+
+ render :show
+ end
+
private
def set_status
@@ -112,6 +129,11 @@ class Api::V1::StatusesController < ApiController
raise ActiveRecord::RecordNotFound unless @status.permitted?(current_account)
end
+ def set_conversation
+ @conversation = @status.conversation
+ raise Mastodon::ValidationError if @conversation.nil?
+ end
+
def status_params
params.permit(:status, :in_reply_to_id, :sensitive, :spoiler_text, :visibility, media_ids: [])
end
diff --git a/app/controllers/api_controller.rb b/app/controllers/api_controller.rb
index 957e3c315..1c67b6fdc 100644
--- a/app/controllers/api_controller.rb
+++ b/app/controllers/api_controller.rb
@@ -93,11 +93,14 @@ class ApiController < ApplicationController
if current_account.nil?
@reblogs_map = {}
@favourites_map = {}
+ @mutes_map = {}
return
end
- status_ids = statuses.compact.flat_map { |s| [s.id, s.reblog_of_id] }.uniq
- @reblogs_map = Status.reblogs_map(status_ids, current_account)
- @favourites_map = Status.favourites_map(status_ids, current_account)
+ status_ids = statuses.compact.flat_map { |s| [s.id, s.reblog_of_id] }.uniq
+ conversation_ids = statuses.compact.map(&:conversation_id).compact.uniq
+ @reblogs_map = Status.reblogs_map(status_ids, current_account)
+ @favourites_map = Status.favourites_map(status_ids, current_account)
+ @mutes_map = Status.mutes_map(conversation_ids, current_account)
end
end
diff --git a/app/javascript/mastodon/actions/statuses.js b/app/javascript/mastodon/actions/statuses.js
index 19df2c36c..5eb9bf817 100644
--- a/app/javascript/mastodon/actions/statuses.js
+++ b/app/javascript/mastodon/actions/statuses.js
@@ -15,6 +15,14 @@ export const CONTEXT_FETCH_REQUEST = 'CONTEXT_FETCH_REQUEST';
export const CONTEXT_FETCH_SUCCESS = 'CONTEXT_FETCH_SUCCESS';
export const CONTEXT_FETCH_FAIL = 'CONTEXT_FETCH_FAIL';
+export const STATUS_MUTE_REQUEST = 'STATUS_MUTE_REQUEST';
+export const STATUS_MUTE_SUCCESS = 'STATUS_MUTE_SUCCESS';
+export const STATUS_MUTE_FAIL = 'STATUS_MUTE_FAIL';
+
+export const STATUS_UNMUTE_REQUEST = 'STATUS_UNMUTE_REQUEST';
+export const STATUS_UNMUTE_SUCCESS = 'STATUS_UNMUTE_SUCCESS';
+export const STATUS_UNMUTE_FAIL = 'STATUS_UNMUTE_FAIL';
+
export function fetchStatusRequest(id, skipLoading) {
return {
type: STATUS_FETCH_REQUEST,
@@ -139,3 +147,71 @@ export function fetchContextFail(id, error) {
skipAlert: true
};
};
+
+export function muteStatus(id) {
+ return (dispatch, getState) => {
+ dispatch(muteStatusRequest(id));
+
+ api(getState).post(`/api/v1/statuses/${id}/mute`).then(response => {
+ dispatch(muteStatusSuccess(id));
+ }).catch(error => {
+ dispatch(muteStatusFail(id, error));
+ });
+ };
+};
+
+export function muteStatusRequest(id) {
+ return {
+ type: STATUS_MUTE_REQUEST,
+ id
+ };
+};
+
+export function muteStatusSuccess(id) {
+ return {
+ type: STATUS_MUTE_SUCCESS,
+ id
+ };
+};
+
+export function muteStatusFail(id, error) {
+ return {
+ type: STATUS_MUTE_FAIL,
+ id,
+ error
+ };
+};
+
+export function unmuteStatus(id) {
+ return (dispatch, getState) => {
+ dispatch(unmuteStatusRequest(id));
+
+ api(getState).post(`/api/v1/statuses/${id}/unmute`).then(response => {
+ dispatch(unmuteStatusSuccess(id));
+ }).catch(error => {
+ dispatch(unmuteStatusFail(id, error));
+ });
+ };
+};
+
+export function unmuteStatusRequest(id) {
+ return {
+ type: STATUS_UNMUTE_REQUEST,
+ id
+ };
+};
+
+export function unmuteStatusSuccess(id) {
+ return {
+ type: STATUS_UNMUTE_SUCCESS,
+ id
+ };
+};
+
+export function unmuteStatusFail(id, error) {
+ return {
+ type: STATUS_UNMUTE_FAIL,
+ id,
+ error
+ };
+};
diff --git a/app/javascript/mastodon/components/status_action_bar.js b/app/javascript/mastodon/components/status_action_bar.js
index b3d5e442c..4d077fb98 100644
--- a/app/javascript/mastodon/components/status_action_bar.js
+++ b/app/javascript/mastodon/components/status_action_bar.js
@@ -16,7 +16,9 @@ const messages = defineMessages({
cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
open: { id: 'status.open', defaultMessage: 'Expand this status' },
- report: { id: 'status.report', defaultMessage: 'Report @{name}' }
+ report: { id: 'status.report', defaultMessage: 'Report @{name}' },
+ muteConversation: { id: 'status.mute_conversation', defaultMessage: 'Mute conversation' },
+ unmuteConversation: { id: 'status.unmute_conversation', defaultMessage: 'Unmute conversation' },
});
class StatusActionBar extends React.PureComponent {
@@ -35,7 +37,9 @@ class StatusActionBar extends React.PureComponent {
onMute: PropTypes.func,
onBlock: PropTypes.func,
onReport: PropTypes.func,
+ onMuteConversation: PropTypes.func,
me: PropTypes.number.isRequired,
+ withDismiss: PropTypes.bool,
intl: PropTypes.object.isRequired
};
@@ -76,9 +80,14 @@ class StatusActionBar extends React.PureComponent {
this.context.router.push('/report');
}
+ handleConversationMuteClick = () => {
+ this.props.onMuteConversation(this.props.status);
+ }
+
render () {
- const { status, me, intl } = this.props;
+ const { status, me, intl, withDismiss } = this.props;
const reblogDisabled = status.get('visibility') === 'private' || status.get('visibility') === 'direct';
+ const mutingConversation = status.get('muted');
let menu = [];
let reblogIcon = 'retweet';
@@ -88,6 +97,11 @@ class StatusActionBar extends React.PureComponent {
menu.push({ text: intl.formatMessage(messages.open), action: this.handleOpen });
menu.push(null);
+ if (withDismiss) {
+ menu.push({ text: intl.formatMessage(mutingConversation ? messages.unmuteConversation : messages.muteConversation), action: this.handleConversationMuteClick });
+ menu.push(null);
+ }
+
if (status.getIn(['account', 'id']) === me) {
menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick });
} else {
diff --git a/app/javascript/mastodon/containers/status_container.js b/app/javascript/mastodon/containers/status_container.js
index eb1f1ab79..8b7d6dc53 100644
--- a/app/javascript/mastodon/containers/status_container.js
+++ b/app/javascript/mastodon/containers/status_container.js
@@ -16,7 +16,7 @@ import {
blockAccount,
muteAccount
} from '../actions/accounts';
-import { deleteStatus } from '../actions/statuses';
+import { muteStatus, unmuteStatus, deleteStatus } from '../actions/statuses';
import { initReport } from '../actions/reports';
import { openModal } from '../actions/modal';
import { createSelector } from 'reselect'
@@ -113,6 +113,14 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
}));
},
+ onMuteConversation (status) {
+ if (status.get('muted')) {
+ dispatch(unmuteStatus(status.get('id')));
+ } else {
+ dispatch(muteStatus(status.get('id')));
+ }
+ },
+
});
export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Status));
diff --git a/app/javascript/mastodon/features/notifications/components/notification.js b/app/javascript/mastodon/features/notifications/components/notification.js
index 48a0e0381..e20be99ea 100644
--- a/app/javascript/mastodon/features/notifications/components/notification.js
+++ b/app/javascript/mastodon/features/notifications/components/notification.js
@@ -32,7 +32,7 @@ class Notification extends ImmutablePureComponent {
}
renderMention (notification) {
- return ;
+ return ;
}
renderFavourite (notification, link) {
@@ -45,7 +45,7 @@ class Notification extends ImmutablePureComponent {
-
+
);
}
@@ -60,7 +60,7 @@ class Notification extends ImmutablePureComponent {
-
+
);
}
diff --git a/app/javascript/mastodon/features/status/components/action_bar.js b/app/javascript/mastodon/features/status/components/action_bar.js
index 0ed149f80..9779f4965 100644
--- a/app/javascript/mastodon/features/status/components/action_bar.js
+++ b/app/javascript/mastodon/features/status/components/action_bar.js
@@ -12,7 +12,7 @@ const messages = defineMessages({
reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
- report: { id: 'status.report', defaultMessage: 'Report @{name}' }
+ report: { id: 'status.report', defaultMessage: 'Report @{name}' },
});
class ActionBar extends React.PureComponent {
diff --git a/app/javascript/mastodon/features/status/index.js b/app/javascript/mastodon/features/status/index.js
index 3ce55e68e..e2ba1c5b9 100644
--- a/app/javascript/mastodon/features/status/index.js
+++ b/app/javascript/mastodon/features/status/index.js
@@ -171,8 +171,24 @@ class Status extends ImmutablePureComponent {
{ancestors}
-
-
+
+
+
{descendants}
diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json
index e6f6d8c51..539b1c81f 100644
--- a/app/javascript/mastodon/locales/ar.json
+++ b/app/javascript/mastodon/locales/ar.json
@@ -142,6 +142,7 @@
"status.load_more": "حمّل المزيد",
"status.media_hidden": "الصورة مستترة",
"status.mention": "أذكُر @{name}",
+ "status.mute_conversation": "Mute conversation",
"status.open": "وسع هذه المشاركة",
"status.reblog": "رَقِّي",
"status.reblogged_by": "{name} رقى",
@@ -152,6 +153,7 @@
"status.sensitive_warning": "محتوى حساس",
"status.show_less": "إعرض أقلّ",
"status.show_more": "أظهر المزيد",
+ "status.unmute_conversation": "Unmute conversation",
"tabs_bar.compose": "تحرير",
"tabs_bar.federated_timeline": "الموحَّد",
"tabs_bar.home": "الرئيسية",
diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json
index 7d0660c25..5101a8b76 100644
--- a/app/javascript/mastodon/locales/bg.json
+++ b/app/javascript/mastodon/locales/bg.json
@@ -142,6 +142,7 @@
"status.load_more": "Load more",
"status.media_hidden": "Media hidden",
"status.mention": "Споменаване",
+ "status.mute_conversation": "Mute conversation",
"status.open": "Expand this status",
"status.reblog": "Споделяне",
"status.reblogged_by": "{name} сподели",
@@ -152,6 +153,7 @@
"status.sensitive_warning": "Деликатно съдържание",
"status.show_less": "Show less",
"status.show_more": "Show more",
+ "status.unmute_conversation": "Unmute conversation",
"tabs_bar.compose": "Съставяне",
"tabs_bar.federated_timeline": "Federated",
"tabs_bar.home": "Начало",
diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json
index 1c3a8b656..bdef91537 100644
--- a/app/javascript/mastodon/locales/de.json
+++ b/app/javascript/mastodon/locales/de.json
@@ -142,6 +142,7 @@
"status.load_more": "Weitere laden",
"status.media_hidden": "Medien versteckt",
"status.mention": "Erwähnen",
+ "status.mute_conversation": "Mute conversation",
"status.open": "Öffnen",
"status.reblog": "Teilen",
"status.reblogged_by": "{name} teilte",
@@ -152,6 +153,7 @@
"status.sensitive_warning": "Heikle Inhalte",
"status.show_less": "Weniger anzeigen",
"status.show_more": "Mehr anzeigen",
+ "status.unmute_conversation": "Unmute conversation",
"tabs_bar.compose": "Schreiben",
"tabs_bar.federated_timeline": "Föderation",
"tabs_bar.home": "Home",
diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json
index 9163a3563..94a5d456d 100644
--- a/app/javascript/mastodon/locales/defaultMessages.json
+++ b/app/javascript/mastodon/locales/defaultMessages.json
@@ -144,6 +144,14 @@
{
"defaultMessage": "Report @{name}",
"id": "status.report"
+ },
+ {
+ "defaultMessage": "Mute conversation",
+ "id": "status.mute_conversation"
+ },
+ {
+ "defaultMessage": "Unmute conversation",
+ "id": "status.unmute_conversation"
}
],
"path": "app/javascript/mastodon/components/status_action_bar.json"
@@ -184,10 +192,6 @@
"defaultMessage": "Expand video",
"id": "video_player.expand"
},
- {
- "defaultMessage": "Video could not be played",
- "id": "video_player.video_error"
- },
{
"defaultMessage": "Sensitive content",
"id": "status.sensitive_warning"
@@ -199,6 +203,10 @@
{
"defaultMessage": "Media hidden",
"id": "status.media_hidden"
+ },
+ {
+ "defaultMessage": "Video could not be played",
+ "id": "video_player.video_error"
}
],
"path": "app/javascript/mastodon/components/video_player.json"
diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json
index 4dd59b883..bb86e813a 100644
--- a/app/javascript/mastodon/locales/en.json
+++ b/app/javascript/mastodon/locales/en.json
@@ -142,6 +142,7 @@
"status.load_more": "Load more",
"status.media_hidden": "Media hidden",
"status.mention": "Mention @{name}",
+ "status.mute_conversation": "Mute conversation",
"status.open": "Expand this status",
"status.reblog": "Boost",
"status.reblogged_by": "{name} boosted",
@@ -152,6 +153,7 @@
"status.sensitive_warning": "Sensitive content",
"status.show_less": "Show less",
"status.show_more": "Show more",
+ "status.unmute_conversation": "Unmute conversation",
"tabs_bar.compose": "Compose",
"tabs_bar.federated_timeline": "Federated",
"tabs_bar.home": "Home",
diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json
index 205af27e1..2e846cd2a 100644
--- a/app/javascript/mastodon/locales/eo.json
+++ b/app/javascript/mastodon/locales/eo.json
@@ -142,6 +142,7 @@
"status.load_more": "Load more",
"status.media_hidden": "Media hidden",
"status.mention": "Mencii @{name}",
+ "status.mute_conversation": "Mute conversation",
"status.open": "Expand this status",
"status.reblog": "Diskonigi",
"status.reblogged_by": "{name} diskonigita",
@@ -152,6 +153,7 @@
"status.sensitive_warning": "Tikla enhavo",
"status.show_less": "Show less",
"status.show_more": "Show more",
+ "status.unmute_conversation": "Unmute conversation",
"tabs_bar.compose": "Ekskribi",
"tabs_bar.federated_timeline": "Federated",
"tabs_bar.home": "Hejmo",
diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json
index fec4b391e..447b384e4 100644
--- a/app/javascript/mastodon/locales/es.json
+++ b/app/javascript/mastodon/locales/es.json
@@ -142,6 +142,7 @@
"status.load_more": "Load more",
"status.media_hidden": "Media hidden",
"status.mention": "Mencionar",
+ "status.mute_conversation": "Mute conversation",
"status.open": "Expandir estado",
"status.reblog": "Retoot",
"status.reblogged_by": "Retooteado por {name}",
@@ -152,6 +153,7 @@
"status.sensitive_warning": "Contenido sensible",
"status.show_less": "Mostrar menos",
"status.show_more": "Mostrar más",
+ "status.unmute_conversation": "Unmute conversation",
"tabs_bar.compose": "Redactar",
"tabs_bar.federated_timeline": "Federated",
"tabs_bar.home": "Inicio",
diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json
index 7820da5f6..680332845 100644
--- a/app/javascript/mastodon/locales/fa.json
+++ b/app/javascript/mastodon/locales/fa.json
@@ -142,6 +142,7 @@
"status.load_more": "بیشتر نشان بده",
"status.media_hidden": "تصویر پنهان شده",
"status.mention": "نامبردن از @{name}",
+ "status.mute_conversation": "Mute conversation",
"status.open": "این نوشته را باز کن",
"status.reblog": "بازبوقیدن",
"status.reblogged_by": "{name} بازبوقید",
@@ -152,6 +153,7 @@
"status.sensitive_warning": "محتوای حساس",
"status.show_less": "نهفتن",
"status.show_more": "نمایش",
+ "status.unmute_conversation": "Unmute conversation",
"tabs_bar.compose": "بنویسید",
"tabs_bar.federated_timeline": "همگانی",
"tabs_bar.home": "خانه",
diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json
index 2621baa51..dc453e42e 100644
--- a/app/javascript/mastodon/locales/fi.json
+++ b/app/javascript/mastodon/locales/fi.json
@@ -142,6 +142,7 @@
"status.load_more": "Load more",
"status.media_hidden": "Media hidden",
"status.mention": "Mainitse @{name}",
+ "status.mute_conversation": "Mute conversation",
"status.open": "Expand this status",
"status.reblog": "Buustaa",
"status.reblogged_by": "{name} buustasi",
@@ -152,6 +153,7 @@
"status.sensitive_warning": "Arkaluontoista sisältöä",
"status.show_less": "Show less",
"status.show_more": "Show more",
+ "status.unmute_conversation": "Unmute conversation",
"tabs_bar.compose": "Luo",
"tabs_bar.federated_timeline": "Federated",
"tabs_bar.home": "Koti",
diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json
index f80cf5a71..6a840fc3e 100644
--- a/app/javascript/mastodon/locales/fr.json
+++ b/app/javascript/mastodon/locales/fr.json
@@ -142,6 +142,7 @@
"status.load_more": "Charger plus",
"status.media_hidden": "Média caché",
"status.mention": "Mentionner",
+ "status.mute_conversation": "Mute conversation",
"status.open": "Déplier ce statut",
"status.reblog": "Partager",
"status.reblogged_by": "{name} a partagé :",
@@ -152,6 +153,7 @@
"status.sensitive_warning": "Contenu délicat",
"status.show_less": "Replier",
"status.show_more": "Déplier",
+ "status.unmute_conversation": "Unmute conversation",
"tabs_bar.compose": "Composer",
"tabs_bar.federated_timeline": "Fil public global",
"tabs_bar.home": "Accueil",
diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json
index e428dfdf2..0cc5f4566 100644
--- a/app/javascript/mastodon/locales/he.json
+++ b/app/javascript/mastodon/locales/he.json
@@ -142,6 +142,7 @@
"status.load_more": "עוד",
"status.media_hidden": "מדיה מוסתרת",
"status.mention": "פניה אל @{name}",
+ "status.mute_conversation": "Mute conversation",
"status.open": "הרחבת הודעה",
"status.reblog": "הדהוד",
"status.reblogged_by": "הודהד על ידי {name}",
@@ -152,6 +153,7 @@
"status.sensitive_warning": "תוכן רגיש",
"status.show_less": "הראה פחות",
"status.show_more": "הראה יותר",
+ "status.unmute_conversation": "Unmute conversation",
"tabs_bar.compose": "חיבור",
"tabs_bar.federated_timeline": "ציר זמן בין-קהילתי",
"tabs_bar.home": "בבית",
diff --git a/app/javascript/mastodon/locales/hr.json b/app/javascript/mastodon/locales/hr.json
index 726ad9609..7f068b2a2 100644
--- a/app/javascript/mastodon/locales/hr.json
+++ b/app/javascript/mastodon/locales/hr.json
@@ -142,6 +142,7 @@
"status.load_more": "Učitaj više",
"status.media_hidden": "Sakriven media sadržaj",
"status.mention": "Spomeni @{name}",
+ "status.mute_conversation": "Mute conversation",
"status.open": "Proširi ovaj status",
"status.reblog": "Podigni",
"status.reblogged_by": "{name} je podigao",
@@ -152,6 +153,7 @@
"status.sensitive_warning": "Osjetljiv sadržaj",
"status.show_less": "Pokaži manje",
"status.show_more": "Pokaži više",
+ "status.unmute_conversation": "Unmute conversation",
"tabs_bar.compose": "Sastavi",
"tabs_bar.federated_timeline": "Federalni",
"tabs_bar.home": "Dom",
diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json
index dd283d736..0dbe2dabb 100644
--- a/app/javascript/mastodon/locales/hu.json
+++ b/app/javascript/mastodon/locales/hu.json
@@ -142,6 +142,7 @@
"status.load_more": "Load more",
"status.media_hidden": "Media hidden",
"status.mention": "Említés",
+ "status.mute_conversation": "Mute conversation",
"status.open": "Expand this status",
"status.reblog": "Reblog",
"status.reblogged_by": "{name} reblogolta",
@@ -152,6 +153,7 @@
"status.sensitive_warning": "Érzékeny tartalom",
"status.show_less": "Show less",
"status.show_more": "Show more",
+ "status.unmute_conversation": "Unmute conversation",
"tabs_bar.compose": "Összeállítás",
"tabs_bar.federated_timeline": "Federated",
"tabs_bar.home": "Kezdőlap",
diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json
index ce0399a65..bdba4b5a8 100644
--- a/app/javascript/mastodon/locales/id.json
+++ b/app/javascript/mastodon/locales/id.json
@@ -142,6 +142,7 @@
"status.load_more": "Tampilkan semua",
"status.media_hidden": "Media disembunyikan",
"status.mention": "Balasan @{name}",
+ "status.mute_conversation": "Mute conversation",
"status.open": "Tampilkan status ini",
"status.reblog": "Boost",
"status.reblogged_by": "di-boost {name}",
@@ -152,6 +153,7 @@
"status.sensitive_warning": "Konten sensitif",
"status.show_less": "Tampilkan lebih sedikit",
"status.show_more": "Tampilkan semua",
+ "status.unmute_conversation": "Unmute conversation",
"tabs_bar.compose": "Tulis",
"tabs_bar.federated_timeline": "Gabungan",
"tabs_bar.home": "Beranda",
diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json
index 5f63f2a89..5b4391688 100644
--- a/app/javascript/mastodon/locales/io.json
+++ b/app/javascript/mastodon/locales/io.json
@@ -142,6 +142,7 @@
"status.load_more": "Kargar pluse",
"status.media_hidden": "Kontenajo celita",
"status.mention": "Mencionar @{name}",
+ "status.mute_conversation": "Mute conversation",
"status.open": "Detaligar ca mesajo",
"status.reblog": "Repetar",
"status.reblogged_by": "{name} repetita",
@@ -152,6 +153,7 @@
"status.sensitive_warning": "Trubliva kontenajo",
"status.show_less": "Montrar mine",
"status.show_more": "Montrar plue",
+ "status.unmute_conversation": "Unmute conversation",
"tabs_bar.compose": "Kompozar",
"tabs_bar.federated_timeline": "Federata",
"tabs_bar.home": "Hemo",
diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json
index 38d23f44c..17b777579 100644
--- a/app/javascript/mastodon/locales/it.json
+++ b/app/javascript/mastodon/locales/it.json
@@ -142,6 +142,7 @@
"status.load_more": "Mostra di più",
"status.media_hidden": "Allegato nascosto",
"status.mention": "Nomina @{name}",
+ "status.mute_conversation": "Mute conversation",
"status.open": "Espandi questo post",
"status.reblog": "Condividi",
"status.reblogged_by": "{name} ha condiviso",
@@ -152,6 +153,7 @@
"status.sensitive_warning": "Materiale sensibile",
"status.show_less": "Mostra meno",
"status.show_more": "Mostra di più",
+ "status.unmute_conversation": "Unmute conversation",
"tabs_bar.compose": "Scrivi",
"tabs_bar.federated_timeline": "Federazione",
"tabs_bar.home": "Home",
diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json
index 0c252535f..ee836a986 100644
--- a/app/javascript/mastodon/locales/ja.json
+++ b/app/javascript/mastodon/locales/ja.json
@@ -142,6 +142,7 @@
"status.load_more": "もっと見る",
"status.media_hidden": "非表示のメデイア",
"status.mention": "返信",
+ "status.mute_conversation": "Mute conversation",
"status.open": "詳細を表示",
"status.reblog": "ブースト",
"status.reblogged_by": "{name} さんにブーストされました",
@@ -152,6 +153,7 @@
"status.sensitive_warning": "閲覧注意",
"status.show_less": "隠す",
"status.show_more": "もっと見る",
+ "status.unmute_conversation": "Unmute conversation",
"tabs_bar.compose": "投稿",
"tabs_bar.federated_timeline": "連合",
"tabs_bar.home": "ホーム",
diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json
index 76e47e46a..82164d4ea 100644
--- a/app/javascript/mastodon/locales/nl.json
+++ b/app/javascript/mastodon/locales/nl.json
@@ -140,6 +140,7 @@
"status.load_more": "Meer laden",
"status.media_hidden": "Media verborgen",
"status.mention": "Vermeld @{name}",
+ "status.mute_conversation": "Mute conversation",
"status.open": "Toot volledig tonen",
"status.reblog": "Boost",
"status.reblogged_by": "{name} boostte",
@@ -150,6 +151,7 @@
"status.sensitive_warning": "Gevoelige inhoud",
"status.show_less": "Minder tonen",
"status.show_more": "Meer tonen",
+ "status.unmute_conversation": "Unmute conversation",
"tabs_bar.compose": "Schrijven",
"tabs_bar.federated_timeline": "Globaal",
"tabs_bar.home": "Jouw tijdlijn",
diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json
index 4254dd2d4..9fcf7dade 100644
--- a/app/javascript/mastodon/locales/no.json
+++ b/app/javascript/mastodon/locales/no.json
@@ -142,6 +142,7 @@
"status.load_more": "Last mer",
"status.media_hidden": "Media skjult",
"status.mention": "Nevn @{name}",
+ "status.mute_conversation": "Mute conversation",
"status.open": "Utvid denne statusen",
"status.reblog": "Fremhev",
"status.reblogged_by": "Fremhevd av {name}",
@@ -152,6 +153,7 @@
"status.sensitive_warning": "Følsomt innhold",
"status.show_less": "Vis mindre",
"status.show_more": "Vis mer",
+ "status.unmute_conversation": "Unmute conversation",
"tabs_bar.compose": "Komponer",
"tabs_bar.federated_timeline": "Felles",
"tabs_bar.home": "Hjem",
diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json
index 6830bc093..26b44af71 100644
--- a/app/javascript/mastodon/locales/oc.json
+++ b/app/javascript/mastodon/locales/oc.json
@@ -142,6 +142,7 @@
"status.load_more": "Cargar mai",
"status.media_hidden": "Mèdia rescondut",
"status.mention": "Mencionar",
+ "status.mute_conversation": "Mute conversation",
"status.open": "Desplegar aqueste estatut",
"status.reblog": "Partejar",
"status.reblogged_by": "{name} a partejat :",
@@ -152,6 +153,7 @@
"status.sensitive_warning": "Contengut embarrassant",
"status.show_less": "Tornar plegar",
"status.show_more": "Desplegar",
+ "status.unmute_conversation": "Unmute conversation",
"tabs_bar.compose": "Compausar",
"tabs_bar.federated_timeline": "Fil public global",
"tabs_bar.home": "Acuèlh",
diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json
index 65c7443d3..4c4d17b53 100644
--- a/app/javascript/mastodon/locales/pl.json
+++ b/app/javascript/mastodon/locales/pl.json
@@ -142,6 +142,7 @@
"status.load_more": "Załaduj więcej",
"status.media_hidden": "Zawartość multimedialna ukryta",
"status.mention": "Wspomnij o @{name}",
+ "status.mute_conversation": "Mute conversation",
"status.open": "Rozszerz ten status",
"status.reblog": "Podbij",
"status.reblogged_by": "{name} podbił",
@@ -152,6 +153,7 @@
"status.sensitive_warning": "Wrażliwa zawartość",
"status.show_less": "Pokaż mniej",
"status.show_more": "Pokaż więcej",
+ "status.unmute_conversation": "Unmute conversation",
"tabs_bar.compose": "Napisz",
"tabs_bar.federated_timeline": "Globalne",
"tabs_bar.home": "Strona główna",
diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json
index 999742e5b..74856015b 100644
--- a/app/javascript/mastodon/locales/pt-BR.json
+++ b/app/javascript/mastodon/locales/pt-BR.json
@@ -142,6 +142,7 @@
"status.load_more": "Carregar mais",
"status.media_hidden": "Media escondida",
"status.mention": "Mencionar @{name}",
+ "status.mute_conversation": "Mute conversation",
"status.open": "Expandir",
"status.reblog": "Partilhar",
"status.reblogged_by": "{name} partilhou",
@@ -152,6 +153,7 @@
"status.sensitive_warning": "Conteúdo sensível",
"status.show_less": "Mostrar menos",
"status.show_more": "Mostrar mais",
+ "status.unmute_conversation": "Unmute conversation",
"tabs_bar.compose": "Criar",
"tabs_bar.federated_timeline": "Global",
"tabs_bar.home": "Home",
diff --git a/app/javascript/mastodon/locales/pt.json b/app/javascript/mastodon/locales/pt.json
index 999742e5b..74856015b 100644
--- a/app/javascript/mastodon/locales/pt.json
+++ b/app/javascript/mastodon/locales/pt.json
@@ -142,6 +142,7 @@
"status.load_more": "Carregar mais",
"status.media_hidden": "Media escondida",
"status.mention": "Mencionar @{name}",
+ "status.mute_conversation": "Mute conversation",
"status.open": "Expandir",
"status.reblog": "Partilhar",
"status.reblogged_by": "{name} partilhou",
@@ -152,6 +153,7 @@
"status.sensitive_warning": "Conteúdo sensível",
"status.show_less": "Mostrar menos",
"status.show_more": "Mostrar mais",
+ "status.unmute_conversation": "Unmute conversation",
"tabs_bar.compose": "Criar",
"tabs_bar.federated_timeline": "Global",
"tabs_bar.home": "Home",
diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json
index fc73a280c..3ae78cd01 100644
--- a/app/javascript/mastodon/locales/ru.json
+++ b/app/javascript/mastodon/locales/ru.json
@@ -142,6 +142,7 @@
"status.load_more": "Показать еще",
"status.media_hidden": "Медиаконтент скрыт",
"status.mention": "Упомянуть @{name}",
+ "status.mute_conversation": "Mute conversation",
"status.open": "Развернуть статус",
"status.reblog": "Продвинуть",
"status.reblogged_by": "{name} продвинул(а)",
@@ -152,6 +153,7 @@
"status.sensitive_warning": "Чувствительный контент",
"status.show_less": "Свернуть",
"status.show_more": "Развернуть",
+ "status.unmute_conversation": "Unmute conversation",
"tabs_bar.compose": "Написать",
"tabs_bar.federated_timeline": "Глобальная",
"tabs_bar.home": "Главная",
diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json
index f483762b4..b2c4036d6 100644
--- a/app/javascript/mastodon/locales/tr.json
+++ b/app/javascript/mastodon/locales/tr.json
@@ -142,6 +142,7 @@
"status.load_more": "Daha fazla",
"status.media_hidden": "Gizli görsel",
"status.mention": "Bahset @{name}",
+ "status.mute_conversation": "Mute conversation",
"status.open": "Bu gönderiyi genişlet",
"status.reblog": "Boost'la",
"status.reblogged_by": "{name} boost etti",
@@ -152,6 +153,7 @@
"status.sensitive_warning": "Hassas içerik",
"status.show_less": "Daha azı",
"status.show_more": "Daha fazlası",
+ "status.unmute_conversation": "Unmute conversation",
"tabs_bar.compose": "Oluştur",
"tabs_bar.federated_timeline": "Federe",
"tabs_bar.home": "Ana sayfa",
diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json
index e4c4cf2eb..a2c50e9e0 100644
--- a/app/javascript/mastodon/locales/uk.json
+++ b/app/javascript/mastodon/locales/uk.json
@@ -142,6 +142,7 @@
"status.load_more": "Завантажити більше",
"status.media_hidden": "Медіаконтент приховано",
"status.mention": "Згадати",
+ "status.mute_conversation": "Mute conversation",
"status.open": "Розгорнути допис",
"status.reblog": "Передмухнути",
"status.reblogged_by": "{name} передмухнув(-ла)",
@@ -152,6 +153,7 @@
"status.sensitive_warning": "Непристойний зміст",
"status.show_less": "Згорнути",
"status.show_more": "Розгорнути",
+ "status.unmute_conversation": "Unmute conversation",
"tabs_bar.compose": "Написати",
"tabs_bar.federated_timeline": "Глобальна",
"tabs_bar.home": "Головна",
diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json
index fa32ebc5e..741689968 100644
--- a/app/javascript/mastodon/locales/zh-CN.json
+++ b/app/javascript/mastodon/locales/zh-CN.json
@@ -142,6 +142,7 @@
"status.load_more": "加载更多",
"status.media_hidden": "隐藏媒体内容",
"status.mention": "提及 @{name}",
+ "status.mute_conversation": "Mute conversation",
"status.open": "展开嘟文",
"status.reblog": "转嘟",
"status.reblogged_by": "{name} 转嘟",
@@ -152,6 +153,7 @@
"status.sensitive_warning": "敏感内容",
"status.show_less": "减少显示",
"status.show_more": "显示更多",
+ "status.unmute_conversation": "Unmute conversation",
"tabs_bar.compose": "撰写",
"tabs_bar.federated_timeline": "跨站",
"tabs_bar.home": "主页",
diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json
index 157fca27f..f55fd8806 100644
--- a/app/javascript/mastodon/locales/zh-HK.json
+++ b/app/javascript/mastodon/locales/zh-HK.json
@@ -142,6 +142,7 @@
"status.load_more": "載入更多",
"status.media_hidden": "隱藏媒體內容",
"status.mention": "提及 @{name}",
+ "status.mute_conversation": "Mute conversation",
"status.open": "展開文章",
"status.reblog": "轉推",
"status.reblogged_by": "{name} 轉推",
@@ -152,6 +153,7 @@
"status.sensitive_warning": "敏感內容",
"status.show_less": "減少顯示",
"status.show_more": "顯示更多",
+ "status.unmute_conversation": "Unmute conversation",
"tabs_bar.compose": "撰寫",
"tabs_bar.federated_timeline": "跨站",
"tabs_bar.home": "主頁",
diff --git a/app/javascript/mastodon/reducers/statuses.js b/app/javascript/mastodon/reducers/statuses.js
index 3ad924c69..282380c37 100644
--- a/app/javascript/mastodon/reducers/statuses.js
+++ b/app/javascript/mastodon/reducers/statuses.js
@@ -10,7 +10,9 @@ import {
} from '../actions/interactions';
import {
STATUS_FETCH_SUCCESS,
- CONTEXT_FETCH_SUCCESS
+ CONTEXT_FETCH_SUCCESS,
+ STATUS_MUTE_SUCCESS,
+ STATUS_UNMUTE_SUCCESS
} from '../actions/statuses';
import {
TIMELINE_REFRESH_SUCCESS,
@@ -103,6 +105,10 @@ export default function statuses(state = initialState, action) {
return state.setIn([action.status.get('id'), 'reblogged'], true);
case REBLOG_FAIL:
return state.setIn([action.status.get('id'), 'reblogged'], false);
+ case STATUS_MUTE_SUCCESS:
+ return state.setIn([action.id, 'muted'], true);
+ case STATUS_UNMUTE_SUCCESS:
+ return state.setIn([action.id, 'muted'], false);
case TIMELINE_REFRESH_SUCCESS:
case TIMELINE_EXPAND_SUCCESS:
case ACCOUNT_TIMELINE_FETCH_SUCCESS:
diff --git a/app/models/account.rb b/app/models/account.rb
index d43cae038..bf3d92a51 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -84,6 +84,7 @@ class Account < ApplicationRecord
# Mute relationships
has_many :mute_relationships, class_name: 'Mute', foreign_key: 'account_id', dependent: :destroy
has_many :muting, -> { order('mutes.id desc') }, through: :mute_relationships, source: :target_account
+ has_many :conversation_mutes
# Media
has_many :media_attachments, dependent: :destroy
@@ -130,6 +131,10 @@ class Account < ApplicationRecord
mute_relationships.find_or_create_by!(target_account: other_account)
end
+ def mute_conversation!(conversation)
+ conversation_mutes.find_or_create_by!(conversation: conversation)
+ end
+
def unfollow!(other_account)
follow = active_relationships.find_by(target_account: other_account)
follow&.destroy
@@ -145,6 +150,11 @@ class Account < ApplicationRecord
mute&.destroy
end
+ def unmute_conversation!(conversation)
+ mute = conversation_mutes.find_by(conversation: conversation)
+ mute&.destroy!
+ end
+
def following?(other_account)
following.include?(other_account)
end
@@ -157,6 +167,10 @@ class Account < ApplicationRecord
muting.include?(other_account)
end
+ def muting_conversation?(conversation)
+ conversation_mutes.where(conversation: conversation).exists?
+ end
+
def requested?(other_account)
follow_requests.where(target_account: other_account).exists?
end
diff --git a/app/models/conversation.rb b/app/models/conversation.rb
index fbec961c7..3715e1049 100644
--- a/app/models/conversation.rb
+++ b/app/models/conversation.rb
@@ -10,7 +10,7 @@
#
class Conversation < ApplicationRecord
- validates :uri, uniqueness: true
+ validates :uri, uniqueness: true, if: :uri
has_many :statuses
diff --git a/app/models/conversation_mute.rb b/app/models/conversation_mute.rb
new file mode 100644
index 000000000..79299b995
--- /dev/null
+++ b/app/models/conversation_mute.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: conversation_mutes
+#
+# id :integer not null, primary key
+# account_id :integer not null
+# conversation_id :integer not null
+#
+
+class ConversationMute < ApplicationRecord
+ belongs_to :account, required: true
+ belongs_to :conversation, required: true
+end
diff --git a/app/models/status.rb b/app/models/status.rb
index 772cef238..7c39a273a 100644
--- a/app/models/status.rb
+++ b/app/models/status.rb
@@ -181,6 +181,10 @@ class Status < ApplicationRecord
select('reblog_of_id').where(reblog_of_id: status_ids).where(account_id: account_id).map { |s| [s.reblog_of_id, true] }.to_h
end
+ def mutes_map(conversation_ids, account_id)
+ ConversationMute.select('conversation_id').where(conversation_id: conversation_ids).where(account_id: account_id).map { |m| [m.conversation_id, true] }.to_h
+ end
+
def reload_stale_associations!(cached_items)
account_ids = []
diff --git a/app/services/notify_service.rb b/app/services/notify_service.rb
index 9c7eb26ef..7b377f6a8 100644
--- a/app/services/notify_service.rb
+++ b/app/services/notify_service.rb
@@ -43,10 +43,19 @@ class NotifyService < BaseService
blocked ||= (@notification.from_account.silenced? && !@recipient.following?(@notification.from_account)) # Hellban
blocked ||= (@recipient.user.settings.interactions['must_be_follower'] && !@notification.from_account.following?(@recipient)) # Options
blocked ||= (@recipient.user.settings.interactions['must_be_following'] && !@recipient.following?(@notification.from_account)) # Options
+ blocked ||= conversation_muted?
blocked ||= send("blocked_#{@notification.type}?") # Type-dependent filters
blocked
end
+ def conversation_muted?
+ if @notification.target_status
+ @recipient.muting_conversation?(@notification.target_status.conversation)
+ else
+ false
+ end
+ end
+
def create_notification
@notification.save!
return unless @notification.browserable?
diff --git a/app/views/api/v1/statuses/show.rabl b/app/views/api/v1/statuses/show.rabl
index 41e8983ef..4b33fb2c3 100644
--- a/app/views/api/v1/statuses/show.rabl
+++ b/app/views/api/v1/statuses/show.rabl
@@ -2,12 +2,14 @@ object @status
extends 'api/v1/statuses/_show'
-node(:favourited, if: proc { !current_account.nil? }) { |status| defined?(@favourites_map) ? @favourites_map[status.id] : current_account.favourited?(status) }
-node(:reblogged, if: proc { !current_account.nil? }) { |status| defined?(@reblogs_map) ? @reblogs_map[status.id] : current_account.reblogged?(status) }
+node(:favourited, if: proc { !current_account.nil? }) { |status| defined?(@favourites_map) ? @favourites_map[status.id] : current_account.favourited?(status) }
+node(:reblogged, if: proc { !current_account.nil? }) { |status| defined?(@reblogs_map) ? @reblogs_map[status.id] : current_account.reblogged?(status) }
+node(:muted, if: proc { !current_account.nil? }) { |status| defined?(@mutes_map) ? @mutes_map[status.conversation_id] : current_account.muting_conversation?(status.conversation) }
child reblog: :reblog do
extends 'api/v1/statuses/_show'
node(:favourited, if: proc { !current_account.nil? }) { |status| defined?(@favourites_map) ? @favourites_map[status.id] : current_account.favourited?(status) }
node(:reblogged, if: proc { !current_account.nil? }) { |status| defined?(@reblogs_map) ? @reblogs_map[status.id] : current_account.reblogged?(status) }
+ node(:muted, if: proc { !current_account.nil? }) { false }
end
diff --git a/config/routes.rb b/config/routes.rb
index 37b3895fd..9ff6a13bb 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -131,6 +131,8 @@ Rails.application.routes.draw do
post :unreblog
post :favourite
post :unfavourite
+ post :mute
+ post :unmute
end
end
diff --git a/db/migrate/20170301222600_create_mutes.rb b/db/migrate/20170301222600_create_mutes.rb
index 8f1bb22f5..4c27eca1e 100644
--- a/db/migrate/20170301222600_create_mutes.rb
+++ b/db/migrate/20170301222600_create_mutes.rb
@@ -7,6 +7,5 @@ class CreateMutes < ActiveRecord::Migration[5.0]
end
add_index :mutes, [:account_id, :target_account_id], unique: true
-
end
end
diff --git a/db/migrate/20170508230434_create_conversation_mutes.rb b/db/migrate/20170508230434_create_conversation_mutes.rb
new file mode 100644
index 000000000..81edf2733
--- /dev/null
+++ b/db/migrate/20170508230434_create_conversation_mutes.rb
@@ -0,0 +1,10 @@
+class CreateConversationMutes < ActiveRecord::Migration[5.0]
+ def change
+ create_table :conversation_mutes do |t|
+ t.integer :account_id, null: false
+ t.bigint :conversation_id, null: false
+ end
+
+ add_index :conversation_mutes, [:account_id, :conversation_id], unique: true
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 7f7fc5978..76624f07a 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20170507141759) do
+ActiveRecord::Schema.define(version: 20170508230434) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -62,6 +62,12 @@ ActiveRecord::Schema.define(version: 20170507141759) do
t.index ["account_id", "target_account_id"], name: "index_blocks_on_account_id_and_target_account_id", unique: true, using: :btree
end
+ create_table "conversation_mutes", force: :cascade do |t|
+ t.integer "account_id", null: false
+ t.bigint "conversation_id", null: false
+ t.index ["account_id", "conversation_id"], name: "index_conversation_mutes_on_account_id_and_conversation_id", unique: true, using: :btree
+ end
+
create_table "conversations", id: :bigserial, force: :cascade do |t|
t.string "uri"
t.datetime "created_at", null: false
diff --git a/spec/controllers/api/v1/statuses_controller_spec.rb b/spec/controllers/api/v1/statuses_controller_spec.rb
index 74faed269..ac3b2dc7d 100644
--- a/spec/controllers/api/v1/statuses_controller_spec.rb
+++ b/spec/controllers/api/v1/statuses_controller_spec.rb
@@ -183,6 +183,39 @@ RSpec.describe Api::V1::StatusesController, type: :controller do
expect(user.account.favourited?(status)).to be false
end
end
+
+ describe 'POST #mute' do
+ let(:status) { Fabricate(:status, account: user.account) }
+
+ before do
+ post :mute, params: { id: status.id }
+ end
+
+ it 'returns http success' do
+ expect(response).to have_http_status(:success)
+ end
+
+ it 'creates a conversation mute' do
+ expect(ConversationMute.find_by(account: user.account, conversation_id: status.conversation_id)).to_not be_nil
+ end
+ end
+
+ describe 'POST #unmute' do
+ let(:status) { Fabricate(:status, account: user.account) }
+
+ before do
+ post :mute, params: { id: status.id }
+ post :unmute, params: { id: status.id }
+ end
+
+ it 'returns http success' do
+ expect(response).to have_http_status(:success)
+ end
+
+ it 'destroys the conversation mute' do
+ expect(ConversationMute.find_by(account: user.account, conversation_id: status.conversation_id)).to be_nil
+ end
+ end
end
context 'without an oauth token' do
diff --git a/spec/fabricators/conversation_mute_fabricator.rb b/spec/fabricators/conversation_mute_fabricator.rb
new file mode 100644
index 000000000..84f131c26
--- /dev/null
+++ b/spec/fabricators/conversation_mute_fabricator.rb
@@ -0,0 +1,2 @@
+Fabricator(:conversation_mute) do
+end
diff --git a/spec/models/conversation_mute_spec.rb b/spec/models/conversation_mute_spec.rb
new file mode 100644
index 000000000..b602e80c1
--- /dev/null
+++ b/spec/models/conversation_mute_spec.rb
@@ -0,0 +1,5 @@
+require 'rails_helper'
+
+RSpec.describe ConversationMute, type: :model do
+
+end
diff --git a/spec/models/status_spec.rb b/spec/models/status_spec.rb
index f9f5c1603..d280525fc 100644
--- a/spec/models/status_spec.rb
+++ b/spec/models/status_spec.rb
@@ -196,6 +196,54 @@ RSpec.describe Status, type: :model do
pending
end
+ describe '.mutes_map' do
+ let(:status) { Fabricate(:status) }
+ let(:account) { Fabricate(:account) }
+
+ subject { Status.mutes_map([status.conversation.id], account) }
+
+ it 'returns a hash' do
+ expect(subject).to be_a Hash
+ end
+
+ it 'contains true value' do
+ account.mute_conversation!(status.conversation)
+ expect(subject[status.conversation.id]).to be true
+ end
+ end
+
+ describe '.favourites_map' do
+ let(:status) { Fabricate(:status) }
+ let(:account) { Fabricate(:account) }
+
+ subject { Status.favourites_map([status], account) }
+
+ it 'returns a hash' do
+ expect(subject).to be_a Hash
+ end
+
+ it 'contains true value' do
+ Fabricate(:favourite, status: status, account: account)
+ expect(subject[status.id]).to be true
+ end
+ end
+
+ describe '.reblogs_map' do
+ let(:status) { Fabricate(:status) }
+ let(:account) { Fabricate(:account) }
+
+ subject { Status.reblogs_map([status], account) }
+
+ it 'returns a hash' do
+ expect(subject).to be_a Hash
+ end
+
+ it 'contains true value' do
+ Fabricate(:status, account: account, reblog: status)
+ expect(subject[status.id]).to be true
+ end
+ end
+
describe '.local_only' do
it 'returns only statuses from local accounts' do
local_account = Fabricate(:account, domain: nil)
diff --git a/spec/services/notify_service_spec.rb b/spec/services/notify_service_spec.rb
index 86848e1ff..032c37a28 100644
--- a/spec/services/notify_service_spec.rb
+++ b/spec/services/notify_service_spec.rb
@@ -7,10 +7,50 @@ RSpec.describe NotifyService do
let(:user) { Fabricate(:user) }
let(:recipient) { user.account }
- let(:activity) { Fabricate(:follow, target_account: recipient) }
+ let(:sender) { Fabricate(:account) }
+ let(:activity) { Fabricate(:follow, account: sender, target_account: recipient) }
it { is_expected.to change(Notification, :count).by(1) }
+ it 'does not notify when sender is blocked' do
+ recipient.block!(sender)
+ is_expected.to_not change(Notification, :count)
+ end
+
+ it 'does not notify when sender is silenced and not followed' do
+ sender.update(silenced: true)
+ is_expected.to_not change(Notification, :count)
+ end
+
+ it 'does not notify when recipient is suspended' do
+ recipient.update(suspended: true)
+ is_expected.to_not change(Notification, :count)
+ end
+
+ context do
+ let(:asshole) { Fabricate(:account, username: 'asshole') }
+ let(:reply_to) { Fabricate(:status, account: asshole) }
+ let(:activity) { Fabricate(:mention, account: recipient, status: Fabricate(:status, account: sender, thread: reply_to)) }
+
+ it 'does not notify when conversation is muted' do
+ recipient.mute_conversation!(activity.status.conversation)
+ is_expected.to_not change(Notification, :count)
+ end
+
+ it 'does not notify when it is a reply to a blocked user' do
+ recipient.block!(asshole)
+ is_expected.to_not change(Notification, :count)
+ end
+ end
+
+ context do
+ let(:sender) { recipient }
+
+ it 'does not notify when recipient is the sender' do
+ is_expected.to_not change(Notification, :count)
+ end
+ end
+
describe 'email' do
before do
ActionMailer::Base.deliveries.clear