]> cat aescling's git repositories - mastodon.git/commitdiff
Add buttons to block and unblock domain (#3127)
authorEugen Rochko <eugen@zeonfederated.com>
Fri, 19 May 2017 19:05:32 +0000 (21:05 +0200)
committerGitHub <noreply@github.com>
Fri, 19 May 2017 19:05:32 +0000 (21:05 +0200)
* Add buttons to block and unblock domain

* Relationship API now returns "domain_blocking" status for accounts,
rename "block entire domain" to "hide entire domain", fix unblocking domain,
do not block notifications from domain-blocked-but-followed people, do
not send Salmons to domain blocked users

* Add test

* Personal domain blocks shouldn't affect Salmon after all, since in this
direction of communication the control is very thin when it comes to
public stuff. Best stay consistent and not affect federation in this way

* Ignore followers and follow request from domain blocked folks,
ensure account domain blocks are not created for empty domain,
and avoid duplicates in validation

* Purge followers when blocking domain (without soft-blocks, since they
are useless here)

* Add tests, fix local timeline being empty when having any domain blocks

46 files changed:
app/controllers/api/v1/accounts_controller.rb
app/controllers/api/v1/domain_blocks_controller.rb
app/javascript/mastodon/actions/domain_blocks.js [new file with mode: 0644]
app/javascript/mastodon/features/account/components/action_bar.js
app/javascript/mastodon/features/account_timeline/components/header.js
app/javascript/mastodon/features/account_timeline/containers/header_container.js
app/javascript/mastodon/locales/ar.json
app/javascript/mastodon/locales/bg.json
app/javascript/mastodon/locales/ca.json
app/javascript/mastodon/locales/de.json
app/javascript/mastodon/locales/defaultMessages.json
app/javascript/mastodon/locales/en.json
app/javascript/mastodon/locales/eo.json
app/javascript/mastodon/locales/es.json
app/javascript/mastodon/locales/fa.json
app/javascript/mastodon/locales/fi.json
app/javascript/mastodon/locales/fr.json
app/javascript/mastodon/locales/he.json
app/javascript/mastodon/locales/hr.json
app/javascript/mastodon/locales/hu.json
app/javascript/mastodon/locales/id.json
app/javascript/mastodon/locales/io.json
app/javascript/mastodon/locales/it.json
app/javascript/mastodon/locales/ja.json
app/javascript/mastodon/locales/nl.json
app/javascript/mastodon/locales/no.json
app/javascript/mastodon/locales/oc.json
app/javascript/mastodon/locales/pl.json
app/javascript/mastodon/locales/pt-BR.json
app/javascript/mastodon/locales/pt.json
app/javascript/mastodon/locales/ru.json
app/javascript/mastodon/locales/tr.json
app/javascript/mastodon/locales/uk.json
app/javascript/mastodon/locales/zh-CN.json
app/javascript/mastodon/locales/zh-HK.json
app/javascript/mastodon/reducers/relationships.js
app/models/account_domain_block.rb
app/models/concerns/account_interactions.rb
app/models/status.rb
app/services/block_domain_from_account_service.rb [new file with mode: 0644]
app/services/notify_service.rb
app/services/process_interaction_service.rb
app/services/send_interaction_service.rb
app/views/api/v1/accounts/relationship.rabl
spec/services/block_domain_from_account_service_spec.rb [new file with mode: 0644]
spec/services/notify_service_spec.rb

index 5724dbaea4f6b09c0517cd30f8199c00e0f8f020..12c7dd2b08c445140771ba28bdf31957da9ac6f2 100644 (file)
@@ -71,11 +71,12 @@ class Api::V1::AccountsController < ApiController
   def block
     BlockService.new.call(current_user.account, @account)
 
-    @following   = { @account.id => false }
-    @followed_by = { @account.id => false }
-    @blocking    = { @account.id => true }
-    @requested   = { @account.id => false }
-    @muting      = { @account.id => current_user.account.muting?(@account.id) }
+    @following       = { @account.id => false }
+    @followed_by     = { @account.id => false }
+    @blocking        = { @account.id => true }
+    @requested       = { @account.id => false }
+    @muting          = { @account.id => current_account.muting?(@account.id) }
+    @domain_blocking = { @account.id => current_account.domain_blocking?(@account.domain) }
 
     render :relationship
   end
@@ -107,12 +108,13 @@ class Api::V1::AccountsController < ApiController
   def relationships
     ids = params[:id].is_a?(Enumerable) ? params[:id].map(&:to_i) : [params[:id].to_i]
 
-    @accounts    = Account.where(id: ids).select('id')
-    @following   = Account.following_map(ids, current_user.account_id)
-    @followed_by = Account.followed_by_map(ids, current_user.account_id)
-    @blocking    = Account.blocking_map(ids, current_user.account_id)
-    @muting      = Account.muting_map(ids, current_user.account_id)
-    @requested   = Account.requested_map(ids, current_user.account_id)
+    @accounts        = Account.where(id: ids).select('id')
+    @following       = Account.following_map(ids, current_user.account_id)
+    @followed_by     = Account.followed_by_map(ids, current_user.account_id)
+    @blocking        = Account.blocking_map(ids, current_user.account_id)
+    @muting          = Account.muting_map(ids, current_user.account_id)
+    @requested       = Account.requested_map(ids, current_user.account_id)
+    @domain_blocking = Account.domain_blocking_map(ids, current_user.account_id)
   end
 
   def search
@@ -128,11 +130,12 @@ class Api::V1::AccountsController < ApiController
   end
 
   def set_relationship
-    @following   = Account.following_map([@account.id], current_user.account_id)
-    @followed_by = Account.followed_by_map([@account.id], current_user.account_id)
-    @blocking    = Account.blocking_map([@account.id], current_user.account_id)
-    @muting      = Account.muting_map([@account.id], current_user.account_id)
-    @requested   = Account.requested_map([@account.id], current_user.account_id)
+    @following       = Account.following_map([@account.id], current_user.account_id)
+    @followed_by     = Account.followed_by_map([@account.id], current_user.account_id)
+    @blocking        = Account.blocking_map([@account.id], current_user.account_id)
+    @muting          = Account.muting_map([@account.id], current_user.account_id)
+    @requested       = Account.requested_map([@account.id], current_user.account_id)
+    @domain_blocking = Account.domain_blocking_map([@account.id], current_user.account_id)
   end
 
   def pagination_params(core_params)
index e14547911c7ad26cba4c9bfce8c8ad6eafdf93a3..f223dd16ee50ecd183a321c6b00ee1db520d0e7e 100644 (file)
@@ -17,7 +17,7 @@ class Api::V1::DomainBlocksController < ApiController
   end
 
   def create
-    current_account.block_domain!(domain_block_params[:domain])
+    BlockDomainFromAccountService.new.call(current_account, domain_block_params[:domain])
     render_empty
   end
 
diff --git a/app/javascript/mastodon/actions/domain_blocks.js b/app/javascript/mastodon/actions/domain_blocks.js
new file mode 100644 (file)
index 0000000..c884981
--- /dev/null
@@ -0,0 +1,117 @@
+import api, { getLinks } from '../api'
+
+export const DOMAIN_BLOCK_REQUEST = 'DOMAIN_BLOCK_REQUEST';
+export const DOMAIN_BLOCK_SUCCESS = 'DOMAIN_BLOCK_SUCCESS';
+export const DOMAIN_BLOCK_FAIL    = 'DOMAIN_BLOCK_FAIL';
+
+export const DOMAIN_UNBLOCK_REQUEST = 'DOMAIN_UNBLOCK_REQUEST';
+export const DOMAIN_UNBLOCK_SUCCESS = 'DOMAIN_UNBLOCK_SUCCESS';
+export const DOMAIN_UNBLOCK_FAIL    = 'DOMAIN_UNBLOCK_FAIL';
+
+export const DOMAIN_BLOCKS_FETCH_REQUEST = 'DOMAIN_BLOCKS_FETCH_REQUEST';
+export const DOMAIN_BLOCKS_FETCH_SUCCESS = 'DOMAIN_BLOCKS_FETCH_SUCCESS';
+export const DOMAIN_BLOCKS_FETCH_FAIL    = 'DOMAIN_BLOCKS_FETCH_FAIL';
+
+export function blockDomain(domain, accountId) {
+  return (dispatch, getState) => {
+    dispatch(blockDomainRequest(domain));
+
+    api(getState).post('/api/v1/domain_blocks', { domain }).then(response => {
+      dispatch(blockDomainSuccess(domain, accountId));
+    }).catch(err => {
+      dispatch(blockDomainFail(domain, err));
+    });
+  };
+};
+
+export function blockDomainRequest(domain) {
+  return {
+    type: DOMAIN_BLOCK_REQUEST,
+    domain
+  };
+};
+
+export function blockDomainSuccess(domain, accountId) {
+  return {
+    type: DOMAIN_BLOCK_SUCCESS,
+    domain,
+    accountId
+  };
+};
+
+export function blockDomainFail(domain, error) {
+  return {
+    type: DOMAIN_BLOCK_FAIL,
+    domain,
+    error
+  };
+};
+
+export function unblockDomain(domain, accountId) {
+  return (dispatch, getState) => {
+    dispatch(unblockDomainRequest(domain));
+
+    api(getState).delete('/api/v1/domain_blocks', { params: { domain } }).then(response => {
+      dispatch(unblockDomainSuccess(domain, accountId));
+    }).catch(err => {
+      dispatch(unblockDomainFail(domain, err));
+    });
+  };
+};
+
+export function unblockDomainRequest(domain) {
+  return {
+    type: DOMAIN_UNBLOCK_REQUEST,
+    domain
+  };
+};
+
+export function unblockDomainSuccess(domain, accountId) {
+  return {
+    type: DOMAIN_UNBLOCK_SUCCESS,
+    domain,
+    accountId
+  };
+};
+
+export function unblockDomainFail(domain, error) {
+  return {
+    type: DOMAIN_UNBLOCK_FAIL,
+    domain,
+    error
+  };
+};
+
+export function fetchDomainBlocks() {
+  return (dispatch, getState) => {
+    dispatch(fetchDomainBlocksRequest());
+
+    api(getState).get().then(response => {
+      const next = getLinks(response).refs.find(link => link.rel === 'next');
+      dispatch(fetchDomainBlocksSuccess(response.data, next ? next.uri : null));
+    }).catch(err => {
+      dispatch(fetchDomainBlocksFail(err));
+    });
+  };
+};
+
+export function fetchDomainBlocksRequest() {
+  return {
+    type: DOMAIN_BLOCKS_FETCH_REQUEST
+  };
+};
+
+export function fetchDomainBlocksSuccess(domains, next) {
+  return {
+    type: DOMAIN_BLOCKS_FETCH_SUCCESS,
+    domains,
+    next
+  };
+};
+
+export function fetchDomainBlocksFail(error) {
+  return {
+    type: DOMAIN_BLOCKS_FETCH_FAIL,
+    error
+  };
+};
index 44ed8af7d18ba53064e7b00cf87371bd8d6af56c..1997bf7b73257f66d42d4e9bb94f4457a8e5ee1c 100644 (file)
@@ -15,7 +15,9 @@ const messages = defineMessages({
   mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' },
   follow: { id: 'account.follow', defaultMessage: 'Follow' },
   report: { id: 'account.report', defaultMessage: 'Report @{name}' },
-  disclaimer: { id: 'account.disclaimer', defaultMessage: 'This user is from another instance. This number may be larger.' }
+  disclaimer: { id: 'account.disclaimer', defaultMessage: 'This user is from another instance. This number may be larger.' },
+  blockDomain: { id: 'account.block_domain', defaultMessage: 'Hide everything from {domain}' },
+  unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unhide {domain}' },
 });
 
 class ActionBar extends React.PureComponent {
@@ -28,6 +30,8 @@ class ActionBar extends React.PureComponent {
     onMention: PropTypes.func.isRequired,
     onReport: PropTypes.func.isRequired,
     onMute: PropTypes.func.isRequired,
+    onBlockDomain: PropTypes.func.isRequired,
+    onUnblockDomain: PropTypes.func.isRequired,
     intl: PropTypes.object.isRequired
   };
 
@@ -59,7 +63,16 @@ class ActionBar extends React.PureComponent {
     }
 
     if (account.get('acct') !== account.get('username')) {
+      const domain = account.get('acct').split('@')[1];
       extraInfo = <abbr title={intl.formatMessage(messages.disclaimer)}>*</abbr>;
+
+      menu.push(null);
+
+      if (account.getIn(['relationship', 'domain_blocking'])) {
+        menu.push({ text: intl.formatMessage(messages.unblockDomain, { domain }), action: this.props.onUnblockDomain });
+      } else {
+        menu.push({ text: intl.formatMessage(messages.blockDomain, { domain }), action: this.props.onBlockDomain });
+      }
     }
 
     return (
index d7226d9b20b8910185713661616b4cd12772430a..f1a0e8d775b4132513ae52385a784d2b9432690e 100644 (file)
@@ -15,7 +15,9 @@ class Header extends ImmutablePureComponent {
     onBlock: PropTypes.func.isRequired,
     onMention: PropTypes.func.isRequired,
     onReport: PropTypes.func.isRequired,
-    onMute: PropTypes.func.isRequired
+    onMute: PropTypes.func.isRequired,
+    onBlockDomain: PropTypes.func.isRequired,
+    onUnblockDomain: PropTypes.func.isRequired,
   };
 
   static contextTypes = {
@@ -43,6 +45,22 @@ class Header extends ImmutablePureComponent {
     this.props.onMute(this.props.account);
   }
 
+  handleBlockDomain = () => {
+    const domain = this.props.account.get('acct').split('@')[1];
+
+    if (!domain) return;
+
+    this.props.onBlockDomain(domain, this.props.account.get('id'));
+  }
+
+  handleUnblockDomain = () => {
+    const domain = this.props.account.get('acct').split('@')[1];
+
+    if (!domain) return;
+
+    this.props.onUnblockDomain(domain, this.props.account.get('id'));
+  }
+
   render () {
     const { account, me } = this.props;
 
@@ -65,6 +83,8 @@ class Header extends ImmutablePureComponent {
           onMention={this.handleMention}
           onReport={this.handleReport}
           onMute={this.handleMute}
+          onBlockDomain={this.handleBlockDomain}
+          onUnblockDomain={this.handleUnblockDomain}
         />
       </div>
     );
index 50999d2e0e63ad61abdfabcfa7bd0a5e1fe6763f..0964efdcfeeb6d560cfbad242816e3339910e52b 100644 (file)
@@ -13,11 +13,13 @@ import {
 import { mentionCompose } from '../../../actions/compose';
 import { initReport } from '../../../actions/reports';
 import { openModal } from '../../../actions/modal';
+import { blockDomain, unblockDomain } from '../../../actions/domain_blocks';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 
 const messages = defineMessages({
   blockConfirm: { id: 'confirmations.block.confirm', defaultMessage: 'Block' },
-  muteConfirm: { id: 'confirmations.mute.confirm', defaultMessage: 'Mute' }
+  muteConfirm: { id: 'confirmations.mute.confirm', defaultMessage: 'Mute' },
+  blockDomainConfirm: { id: 'confirmations.domain_block.confirm', defaultMessage: 'Hide entire domain' },
 });
 
 const makeMapStateToProps = () => {
@@ -70,6 +72,18 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
         onConfirm: () => dispatch(muteAccount(account.get('id')))
       }));
     }
+  },
+
+  onBlockDomain (domain, accountId) {
+    dispatch(openModal('CONFIRM', {
+      message: <FormattedMessage id='confirmations.domain_block.message' defaultMessage='Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.' values={{ domain: <strong>{domain}</strong> }} />,
+      confirm: intl.formatMessage(messages.blockDomainConfirm),
+      onConfirm: () => dispatch(blockDomain(domain, accountId))
+    }));
+  },
+
+  onUnblockDomain (domain, accountId) {
+    dispatch(unblockDomain(domain, accountId));
   }
 });
 
index e30b7e84aec5d25905d368d2f6285c99c6c4e819..2a9d00460a6112570160d19cf52a7c51a2676aac 100644 (file)
@@ -1,17 +1,20 @@
 {
   "account.block": "حظر @{name}",
+  "account.block_domain": "Hide everything from {domain}",
   "account.disclaimer": "هذا المستخدم من مثيل خادم آخر. قد يكون هذا الرقم أكبر.",
   "account.edit_profile": "تعديل الملف الشخصي",
   "account.follow": "تابِع",
   "account.followers": "المتابعون",
   "account.follows": "يتبع",
   "account.follows_you": "يتابعك",
+  "account.media": "Media",
   "account.mention": "أُذكُر @{name}",
   "account.mute": "أكتم @{name}",
   "account.posts": "المشاركات",
   "account.report": "أبلغ عن @{name}",
   "account.requested": "في انتظار الموافقة",
   "account.unblock": "إلغاء الحظر عن @{name}",
+  "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "إلغاء المتابعة",
   "account.unmute": "إلغاء الكتم عن @{name}",
   "boost_modal.combo": "يمكنك ضغط {combo} لتخطّي هذه في المرّة القادمة",
@@ -40,6 +43,8 @@
   "confirmations.block.message": "هل أنت متأكد أنك تريد حجب {name} ؟",
   "confirmations.delete.confirm": "حذف",
   "confirmations.delete.message": "هل أنت متأكد أنك تريد حذف هذا المنشور ؟",
+  "confirmations.domain_block.confirm": "Hide entire domain",
+  "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
   "confirmations.mute.confirm": "أكتم",
   "confirmations.mute.message": "هل أنت متأكد أنك تريد كتم {name} ؟",
   "emoji_button.activity": "الأنشطة",
index 3ca2f7775bb38fe43ac816c7a0014fd58baf04de..a43cd3dabfe7c07431197f8d3b253a3dd133668b 100644 (file)
@@ -1,17 +1,20 @@
 {
   "account.block": "Блокирай",
+  "account.block_domain": "Hide everything from {domain}",
   "account.disclaimer": "This user is from another instance. This number may be larger.",
   "account.edit_profile": "Редактирай профила си",
   "account.follow": "Последвай",
   "account.followers": "Последователи",
   "account.follows": "Следвам",
   "account.follows_you": "Твой последовател",
+  "account.media": "Media",
   "account.mention": "Споменаване",
   "account.mute": "Mute @{name}",
   "account.posts": "Публикации",
   "account.report": "Report @{name}",
   "account.requested": "В очакване на одобрение",
   "account.unblock": "Не блокирай",
+  "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "Не следвай",
   "account.unmute": "Unmute @{name}",
   "boost_modal.combo": "You can press {combo} to skip this next time",
@@ -40,6 +43,8 @@
   "confirmations.block.message": "Are you sure you want to block {name}?",
   "confirmations.delete.confirm": "Delete",
   "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.domain_block.confirm": "Hide entire domain",
+  "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
   "confirmations.mute.confirm": "Mute",
   "confirmations.mute.message": "Are you sure you want to mute {name}?",
   "emoji_button.activity": "Activity",
index f239a3d2fb5f05272059f81d214ccdd19b18fcec..61827220e66b06c1568dca53da85e690afebe0c8 100644 (file)
@@ -1,17 +1,20 @@
 {
   "account.block": "Bloquejar @{name}",
+  "account.block_domain": "Hide everything from {domain}",
   "account.disclaimer": "Aquest usuari és d'un altra instància. Aquest número podria ser més gran.",
   "account.edit_profile": "Editar perfil",
   "account.follow": "Seguir",
   "account.followers": "Seguidors",
   "account.follows": "Seguint",
   "account.follows_you": "et segueix",
+  "account.media": "Media",
   "account.mention": "Esmentar @{name}",
   "account.mute": "Silenciar @{name}",
   "account.posts": "Publicacions",
   "account.report": "Informe @{name}",
   "account.requested": "Esperant aprovació",
   "account.unblock": "Desbloquejar @{name}",
+  "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "Deixar de seguir",
   "account.unmute": "Treure silenci de @{name}",
   "boost_modal.combo": "Pots premer {combo} per saltar-te això el proper cop",
@@ -40,6 +43,8 @@
   "confirmations.block.message": "Estàs segur que vols bloquejar {name}?",
   "confirmations.delete.confirm": "Esborrar",
   "confirmations.delete.message": "Estàs segur que vols esborrar aquest estat?",
+  "confirmations.domain_block.confirm": "Hide entire domain",
+  "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
   "confirmations.mute.confirm": "Silenciar",
   "confirmations.mute.message": "Estàs segur que vols silenciar {name}?",
   "emoji_button.activity": "Activitat",
index 8a5b5e418dcd5f471e12d63aee51848792f8e065..a82bee22d0195cf0477922991fdfd934d57ef678 100644 (file)
@@ -1,17 +1,20 @@
 {
   "account.block": "@{name} blocken",
+  "account.block_domain": "Hide everything from {domain}",
   "account.disclaimer": "Dieser Benutzer ist von einer anderen Instanz. Diese Zahl könnte größer sein.",
   "account.edit_profile": "Profil bearbeiten",
   "account.follow": "Folgen",
   "account.followers": "Folgende",
   "account.follows": "Folgt",
   "account.follows_you": "Folgt dir",
+  "account.media": "Media",
   "account.mention": "@{name} erwähnen",
   "account.mute": "@{name} stummschalten",
   "account.posts": "Beiträge",
   "account.report": "@{name} melden",
   "account.requested": "Warte auf Erlaubnis",
   "account.unblock": "@{name} entblocken",
+  "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "Entfolgen",
   "account.unmute": "@{name} nicht mehr stummschalten",
   "boost_modal.combo": "Du kannst {combo} drücken, um dies beim nächsten Mal zu überspringen",
@@ -40,6 +43,8 @@
   "confirmations.block.message": "Are you sure you want to block {name}?",
   "confirmations.delete.confirm": "Delete",
   "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.domain_block.confirm": "Hide entire domain",
+  "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
   "confirmations.mute.confirm": "Mute",
   "confirmations.mute.message": "Are you sure you want to mute {name}?",
   "emoji_button.activity": "Activity",
index 38d76828d44b8c90cf0e74453c712999746ba80e..67c7cc527ad7101d6157a74cec6ce6c727fed8e7 100644 (file)
     ],
     "path": "app/javascript/mastodon/containers/status_container.json"
   },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Media",
+        "id": "account.media"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/account_gallery/index.json"
+  },
   {
     "descriptors": [
       {
         "defaultMessage": "Mute",
         "id": "confirmations.mute.confirm"
       },
+      {
+        "defaultMessage": "Hide entire domain",
+        "id": "confirmations.domain_block.confirm"
+      },
       {
         "defaultMessage": "Are you sure you want to block {name}?",
         "id": "confirmations.block.message"
       {
         "defaultMessage": "Are you sure you want to mute {name}?",
         "id": "confirmations.mute.message"
+      },
+      {
+        "defaultMessage": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
+        "id": "confirmations.domain_block.message"
       }
     ],
     "path": "app/javascript/mastodon/features/account_timeline/containers/header_container.json"
         "defaultMessage": "This user is from another instance. This number may be larger.",
         "id": "account.disclaimer"
       },
+      {
+        "defaultMessage": "Hide everything from {domain}",
+        "id": "account.block_domain"
+      },
+      {
+        "defaultMessage": "Unhide {domain}",
+        "id": "account.unblock_domain"
+      },
       {
         "defaultMessage": "Posts",
         "id": "account.posts"
index f4a6a7512f6d990afba5cfe0507f5636d7923f4e..6864123aec9555b779d1f3723e4c937a3f67f0d4 100644 (file)
@@ -1,17 +1,20 @@
 {
   "account.block": "Block @{name}",
+  "account.block_domain": "Hide everything from {domain}",
   "account.disclaimer": "This user is from another instance. This number may be larger.",
   "account.edit_profile": "Edit profile",
   "account.follow": "Follow",
   "account.followers": "Followers",
   "account.follows": "Follows",
   "account.follows_you": "Follows you",
+  "account.media": "Media",
   "account.mention": "Mention @{name}",
   "account.mute": "Mute @{name}",
   "account.posts": "Posts",
   "account.report": "Report @{name}",
   "account.requested": "Awaiting approval",
   "account.unblock": "Unblock @{name}",
+  "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "Unfollow",
   "account.unmute": "Unmute @{name}",
   "boost_modal.combo": "You can press {combo} to skip this next time",
@@ -40,6 +43,8 @@
   "confirmations.block.message": "Are you sure you want to block {name}?",
   "confirmations.delete.confirm": "Delete",
   "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.domain_block.confirm": "Hide entire domain",
+  "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
   "confirmations.mute.confirm": "Mute",
   "confirmations.mute.message": "Are you sure you want to mute {name}?",
   "emoji_button.activity": "Activity",
index 904c08cc93fc7b8efc8c5ad2ae8196a05dd2b838..3437bd9e99391452c5a96d0208cd89b3fd8f0774 100644 (file)
@@ -1,17 +1,20 @@
 {
   "account.block": "Bloki @{name}",
+  "account.block_domain": "Hide everything from {domain}",
   "account.disclaimer": "This user is from another instance. This number may be larger.",
   "account.edit_profile": "Redakti la profilon",
   "account.follow": "Sekvi",
   "account.followers": "Sekvantoj",
   "account.follows": "Sekvatoj",
   "account.follows_you": "Sekvas vin",
+  "account.media": "Media",
   "account.mention": "Mencii @{name}",
   "account.mute": "Mute @{name}",
   "account.posts": "Mesaĝoj",
   "account.report": "Report @{name}",
   "account.requested": "Atendas aprobon",
   "account.unblock": "Malbloki @{name}",
+  "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "Malsekvi",
   "account.unmute": "Unmute @{name}",
   "boost_modal.combo": "You can press {combo} to skip this next time",
@@ -40,6 +43,8 @@
   "confirmations.block.message": "Are you sure you want to block {name}?",
   "confirmations.delete.confirm": "Delete",
   "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.domain_block.confirm": "Hide entire domain",
+  "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
   "confirmations.mute.confirm": "Mute",
   "confirmations.mute.message": "Are you sure you want to mute {name}?",
   "emoji_button.activity": "Activity",
index 605c14a098b1baf726f4a95e26eda45fa0ca773b..14b007a3e0c6ca0b2c7032d00f18d619a24de375 100644 (file)
@@ -1,17 +1,20 @@
 {
   "account.block": "Bloquear",
+  "account.block_domain": "Hide everything from {domain}",
   "account.disclaimer": "This user is from another instance. This number may be larger.",
   "account.edit_profile": "Editar perfil",
   "account.follow": "Seguir",
   "account.followers": "Seguidores",
   "account.follows": "Seguir",
   "account.follows_you": "Te sigue",
+  "account.media": "Media",
   "account.mention": "Mencionar",
   "account.mute": "Silenciar",
   "account.posts": "Publicaciones",
   "account.report": "Report @{name}",
   "account.requested": "Esperando aprobación",
   "account.unblock": "Desbloquear",
+  "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "Dejar de seguir",
   "account.unmute": "Unmute @{name}",
   "boost_modal.combo": "You can press {combo} to skip this next time",
@@ -40,6 +43,8 @@
   "confirmations.block.message": "Are you sure you want to block {name}?",
   "confirmations.delete.confirm": "Delete",
   "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.domain_block.confirm": "Hide entire domain",
+  "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
   "confirmations.mute.confirm": "Mute",
   "confirmations.mute.message": "Are you sure you want to mute {name}?",
   "emoji_button.activity": "Activity",
index 72e9386957448838da17062f2b6b6ab9e6f7a240..59266dcd94a9d38fc46fa5c5d52012b5f731448f 100644 (file)
@@ -1,17 +1,20 @@
 {
   "account.block": "مسدودسازی @{name}",
+  "account.block_domain": "Hide everything from {domain}",
   "account.disclaimer": "این کاربر عضو سرور متفاوتی است. شاید عدد واقعی بیشتر از این باشد.",
   "account.edit_profile": "ویرایش نمایه",
   "account.follow": "پی بگیرید",
   "account.followers": "پیگیران",
   "account.follows": "پی می‌گیرد",
   "account.follows_you": "پیگیر شماست",
+  "account.media": "Media",
   "account.mention": "نام‌بردن از @{name}",
   "account.mute": "بی‌صدا کردن @{name}",
   "account.posts": "نوشته‌ها",
   "account.report": "گزارش @{name}",
   "account.requested": "در انتظار پذیرش",
   "account.unblock": "رفع انسداد @{name}",
+  "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "پایان پیگیری",
   "account.unmute": "باصدا کردن @{name}",
   "boost_modal.combo": "دکمهٔ {combo} را بزنید تا دیگر این را نبینید",
@@ -40,6 +43,8 @@
   "confirmations.block.message": "آیا واقعاً می‌خواهید {name} را مسدود کنید؟",
   "confirmations.delete.confirm": "پاک کن",
   "confirmations.delete.message": "آیا واقعاً می‌خواهید این نوشته را پاک کنید؟",
+  "confirmations.domain_block.confirm": "Hide entire domain",
+  "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
   "confirmations.mute.confirm": "بی‌صدا کن",
   "confirmations.mute.message": "آیا واقعاً می‌خواهید {name} را بی‌صدا کنید؟",
   "emoji_button.activity": "فعالیت",
index 42b1fbcb0b07a45d3c9391bb4fe19f322ec4f5e9..c544c955cd3061e1485f7fcf1cf1251cd2a02c0c 100644 (file)
@@ -1,17 +1,20 @@
 {
   "account.block": "Estä @{name}",
+  "account.block_domain": "Hide everything from {domain}",
   "account.disclaimer": "This user is from another instance. This number may be larger.",
   "account.edit_profile": "Muokkaa",
   "account.follow": "Seuraa",
   "account.followers": "Seuraajia",
   "account.follows": "Seuraa",
   "account.follows_you": "Seuraa sinua",
+  "account.media": "Media",
   "account.mention": "Mainitse @{name}",
   "account.mute": "Mute @{name}",
   "account.posts": "Postit",
   "account.report": "Report @{name}",
   "account.requested": "Odottaa hyväksyntää",
   "account.unblock": "Salli @{name}",
+  "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "Lopeta seuraaminen",
   "account.unmute": "Unmute @{name}",
   "boost_modal.combo": "You can press {combo} to skip this next time",
@@ -40,6 +43,8 @@
   "confirmations.block.message": "Are you sure you want to block {name}?",
   "confirmations.delete.confirm": "Delete",
   "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.domain_block.confirm": "Hide entire domain",
+  "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
   "confirmations.mute.confirm": "Mute",
   "confirmations.mute.message": "Are you sure you want to mute {name}?",
   "emoji_button.activity": "Activity",
index c6665f43c138e56c348859316ddf58b50fc05963..d3c292707ffec784395a28f5635e6b4abba12687 100644 (file)
@@ -1,17 +1,20 @@
 {
   "account.block": "Bloquer",
+  "account.block_domain": "Hide everything from {domain}",
   "account.disclaimer": "Ce compte est situé sur une autre instance. Les nombres peuvent être plus grands.",
   "account.edit_profile": "Modifier le profil",
   "account.follow": "Suivre",
   "account.followers": "Abonné⋅e⋅s",
   "account.follows": "Abonnements",
   "account.follows_you": "Vous suit",
+  "account.media": "Media",
   "account.mention": "Mentionner",
   "account.mute": "Masquer",
   "account.posts": "Statuts",
   "account.report": "Signaler",
   "account.requested": "Invitation envoyée",
   "account.unblock": "Débloquer",
+  "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "Ne plus suivre",
   "account.unmute": "Ne plus masquer",
   "boost_modal.combo": "Vous pouvez appuyer sur {combo} pour pouvoir passer ceci, la prochaine fois",
@@ -40,6 +43,8 @@
   "confirmations.block.message": "Confirmez vous le blocage de {name} ?",
   "confirmations.delete.confirm": "Supprimer",
   "confirmations.delete.message": "Confirmez vous la suppression de ce pouet ?",
+  "confirmations.domain_block.confirm": "Hide entire domain",
+  "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
   "confirmations.mute.confirm": "Silencer",
   "confirmations.mute.message": "Confirmez vous la silenciation {name} ?",
   "emoji_button.activity": "Activités",
index 8e4baa0c5bd7150f9344e1ce52b00604d772dceb..0a38065b5536cc1c83d540c352d0545394be715b 100644 (file)
@@ -1,17 +1,20 @@
 {
   "account.block": "חסימת @{name}",
+  "account.block_domain": "Hide everything from {domain}",
   "account.disclaimer": "משתמש זה מגיע מקהילה אחרת. המספר הזה עשוי להיות גדול יותר.",
   "account.edit_profile": "עריכת פרופיל",
   "account.follow": "מעקב",
   "account.followers": "עוקבים",
   "account.follows": "נעקבים",
   "account.follows_you": "במעקב אחריך",
+  "account.media": "Media",
   "account.mention": "אזכור של @{name}",
   "account.mute": "להשתיק את @{name}",
   "account.posts": "הודעות",
   "account.report": "לדווח על @{name}",
   "account.requested": "בהמתנה לאישור",
   "account.unblock": "הסרת חסימה מעל @{name}",
+  "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "הפסקת מעקב",
   "account.unmute": "הפסקת השתקת @{name}",
   "boost_modal.combo": "ניתן להקיש {combo} כדי לדלג בפעם הבאה",
@@ -40,6 +43,8 @@
   "confirmations.block.message": "לחסום את {name}?",
   "confirmations.delete.confirm": "למחוק",
   "confirmations.delete.message": "למחוק את ההודעה?",
+  "confirmations.domain_block.confirm": "Hide entire domain",
+  "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
   "confirmations.mute.confirm": "להשתיק",
   "confirmations.mute.message": "להשתיק את {name}?",
   "emoji_button.activity": "פעילות",
index d263a73b90df159cc53e87c052ec043e836bd900..6d4b43c009a5c5ac5279bc6ad39d152538b54fc5 100644 (file)
@@ -1,17 +1,20 @@
 {
   "account.block": "Blokiraj @{name}",
+  "account.block_domain": "Hide everything from {domain}",
   "account.disclaimer": "Ovaj korisnik je sa druge instance. Ovaj broj bi mogao biti veći.",
   "account.edit_profile": "Uredi profil",
   "account.follow": "Slijedi",
   "account.followers": "Sljedbenici",
   "account.follows": "Slijedi",
   "account.follows_you": "te slijedi",
+  "account.media": "Media",
   "account.mention": "Spomeni @{name}",
   "account.mute": "Utišaj @{name}",
   "account.posts": "Postovi",
   "account.report": "Prijavi @{name}",
   "account.requested": "Čeka pristanak",
   "account.unblock": "Deblokiraj @{name}",
+  "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "Prestani slijediti",
   "account.unmute": "Poništi utišavanje @{name}",
   "boost_modal.combo": "Možeš pritisnuti {combo} kako bi ovo preskočio sljedeći put",
@@ -40,6 +43,8 @@
   "confirmations.block.message": "Are you sure you want to block {name}?",
   "confirmations.delete.confirm": "Delete",
   "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.domain_block.confirm": "Hide entire domain",
+  "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
   "confirmations.mute.confirm": "Mute",
   "confirmations.mute.message": "Are you sure you want to mute {name}?",
   "emoji_button.activity": "Activity",
index c50501d242cb42bdf38464a189266121ae12119b..ebe2d4171791aa11ed6dac4b7e80b178dca7e3a7 100644 (file)
@@ -1,17 +1,20 @@
 {
   "account.block": "Blokkolás",
+  "account.block_domain": "Hide everything from {domain}",
   "account.disclaimer": "This user is from another instance. This number may be larger.",
   "account.edit_profile": "Profil szerkesztése",
   "account.follow": "Követés",
   "account.followers": "Követők",
   "account.follows": "Követve",
   "account.follows_you": "Követnek téged",
+  "account.media": "Media",
   "account.mention": "Említés",
   "account.mute": "Mute @{name}",
   "account.posts": "Posts",
   "account.report": "Report @{name}",
   "account.requested": "Awaiting approval",
   "account.unblock": "Blokkolás levétele",
+  "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "Követés abbahagyása",
   "account.unmute": "Unmute @{name}",
   "boost_modal.combo": "You can press {combo} to skip this next time",
@@ -40,6 +43,8 @@
   "confirmations.block.message": "Are you sure you want to block {name}?",
   "confirmations.delete.confirm": "Delete",
   "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.domain_block.confirm": "Hide entire domain",
+  "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
   "confirmations.mute.confirm": "Mute",
   "confirmations.mute.message": "Are you sure you want to mute {name}?",
   "emoji_button.activity": "Activity",
index ca821a5d5ee07ac757e459d045c141277f17c77b..45eea622b33345f95d49180f8b7d694aec1c34fe 100644 (file)
@@ -1,17 +1,20 @@
 {
   "account.block": "Blokir @{name}",
+  "account.block_domain": "Hide everything from {domain}",
   "account.disclaimer": "Pengguna ini berasal dari server lain. Angka berikut mungkin lebih besar.",
   "account.edit_profile": "Ubah profil",
   "account.follow": "Ikuti",
   "account.followers": "Pengikut",
   "account.follows": "Mengikuti",
   "account.follows_you": "Mengikuti anda",
+  "account.media": "Media",
   "account.mention": "Balasan @{name}",
   "account.mute": "Bisukan @{name}",
   "account.posts": "Postingan",
   "account.report": "Laporkan @{name}",
   "account.requested": "Menunggu persetujuan",
   "account.unblock": "Hapus blokir @{name}",
+  "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "Berhenti mengikuti",
   "account.unmute": "Berhenti membisukan @{name}",
   "boost_modal.combo": "Anda dapat menekan {combo} untuk melewati ini",
@@ -40,6 +43,8 @@
   "confirmations.block.message": "Apa anda yakin ingin memblokir {name}?",
   "confirmations.delete.confirm": "Hapus",
   "confirmations.delete.message": "Apa anda yakin akan menghapus status ini?",
+  "confirmations.domain_block.confirm": "Hide entire domain",
+  "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
   "confirmations.mute.confirm": "Bisukan",
   "confirmations.mute.message": "Apa anda yakin ingin membisukan {name}?",
   "emoji_button.activity": "Aktivitas",
index 34d38868f85b307611de2ba92677a6dcbd87e9a4..f6791d1809037e0be7e02239c99a7347164854d0 100644 (file)
@@ -1,17 +1,20 @@
 {
   "account.block": "Blokusar @{name}",
+  "account.block_domain": "Hide everything from {domain}",
   "account.disclaimer": "Ca uzero esas de altra instaluro. Ca nombro forsan esas plu granda.",
   "account.edit_profile": "Modifikar profilo",
   "account.follow": "Sequar",
   "account.followers": "Sequanti",
   "account.follows": "Sequas",
   "account.follows_you": "Sequas tu",
+  "account.media": "Media",
   "account.mention": "Mencionar @{name}",
   "account.mute": "Celar @{name}",
   "account.posts": "Mesaji",
   "account.report": "Denuncar @{name}",
   "account.requested": "Vartante aprobo",
   "account.unblock": "Desblokusar @{name}",
+  "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "Ne plus sequar",
   "account.unmute": "Ne plus celar @{name}",
   "boost_modal.combo": "Tu povas presar sur {combo} por omisar co en la venonta foyo",
@@ -40,6 +43,8 @@
   "confirmations.block.message": "Are you sure you want to block {name}?",
   "confirmations.delete.confirm": "Delete",
   "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.domain_block.confirm": "Hide entire domain",
+  "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
   "confirmations.mute.confirm": "Mute",
   "confirmations.mute.message": "Are you sure you want to mute {name}?",
   "emoji_button.activity": "Activity",
index 8b92ce86d5249428b2aebb8ad8a194aecee17b25..c09b705ebb5063c254190e49b07ad51cc66533fd 100644 (file)
@@ -1,17 +1,20 @@
 {
   "account.block": "Blocca @{name}",
+  "account.block_domain": "Hide everything from {domain}",
   "account.disclaimer": "Questo utente si trova su un altro server. Questo numero potrebbe essere maggiore.",
   "account.edit_profile": "Modifica profilo",
   "account.follow": "Segui",
   "account.followers": "Seguaci",
   "account.follows": "Segue",
   "account.follows_you": "Ti segue",
+  "account.media": "Media",
   "account.mention": "Menziona @{name}",
   "account.mute": "Silenzia @{name}",
   "account.posts": "Posts",
   "account.report": "Segnala @{name}",
   "account.requested": "In attesa di approvazione",
   "account.unblock": "Sblocca @{name}",
+  "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "Non seguire",
   "account.unmute": "Non silenziare @{name}",
   "boost_modal.combo": "Puoi premere {combo} per saltare questo passaggio la prossima volta",
@@ -40,6 +43,8 @@
   "confirmations.block.message": "Are you sure you want to block {name}?",
   "confirmations.delete.confirm": "Delete",
   "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.domain_block.confirm": "Hide entire domain",
+  "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
   "confirmations.mute.confirm": "Mute",
   "confirmations.mute.message": "Are you sure you want to mute {name}?",
   "emoji_button.activity": "Activity",
index 5f1ac2381d2b0d61a0e9ac8125fcfaf21bfe64b3..38745e20ffa8e45c41cc9bb08ba4e5f2d20cc599 100644 (file)
@@ -1,17 +1,20 @@
 {
   "account.block": "ブロック",
+  "account.block_domain": "Hide everything from {domain}",
   "account.disclaimer": "このユーザーは他のインスタンスに所属しているため、数字が正確で無い場合があります。",
   "account.edit_profile": "プロフィールを編集",
   "account.follow": "フォロー",
   "account.followers": "フォロワー",
   "account.follows": "フォロー",
   "account.follows_you": "フォローされています",
+  "account.media": "Media",
   "account.mention": "返信",
   "account.mute": "ミュート",
   "account.posts": "投稿",
   "account.report": "通報",
   "account.requested": "承認待ち",
   "account.unblock": "ブロック解除",
+  "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "フォロー解除",
   "account.unmute": "ミュート解除",
   "boost_modal.combo": "次からは{combo}を押せば、これをスキップできます。",
@@ -40,6 +43,8 @@
   "confirmations.block.message": "本当に{name}をブロックしますか?",
   "confirmations.delete.confirm": "削除",
   "confirmations.delete.message": "本当に削除しますか?",
+  "confirmations.domain_block.confirm": "Hide entire domain",
+  "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
   "confirmations.mute.confirm": "ミュート",
   "confirmations.mute.message": "本当に{name}をミュートしますか?",
   "emoji_button.activity": "活動",
index e63f527c104efea23362ea9fc602525281da882e..33e51a680170d06ee76f9c3bd924b112ec741a9e 100644 (file)
@@ -1,17 +1,20 @@
 {
   "account.block": "Blokkeer @{name}",
+  "account.block_domain": "Hide everything from {domain}",
   "account.disclaimer": "Deze gebruiker zit op een andere server. Dit getal kan hoger zijn.",
   "account.edit_profile": "Profiel bewerken",
   "account.follow": "Volgen",
   "account.followers": "Volgers",
   "account.follows": "Volgt",
   "account.follows_you": "Volgt jou",
+  "account.media": "Media",
   "account.mention": "Vermeld @{name}",
   "account.mute": "Negeer @{name}",
   "account.posts": "Berichten",
   "account.report": "Rapporteer @{name}",
   "account.requested": "Wacht op goedkeuring",
   "account.unblock": "Deblokkeer @{name}",
+  "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "Ontvolgen",
   "account.unmute": "Negeer @{name} niet meer",
   "boost_modal.combo": "Je kunt {combo} klikken om dit de volgende keer over te slaan",
@@ -40,6 +43,8 @@
   "confirmations.block.message": "Weet je zeker dat je {name} wilt blokkeren?",
   "confirmations.delete.confirm": "Verwijderen",
   "confirmations.delete.message": "Weet je zeker dat je deze toot wilt verwijderen?",
+  "confirmations.domain_block.confirm": "Hide entire domain",
+  "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
   "confirmations.mute.confirm": "Negeren",
   "confirmations.mute.message": "Weet je zeker dat je {name} wilt negeren?",
   "emoji_button.activity": "Activiteiten",
index d3fac55bdb25e3bd3c87e2a028fa8ec389aa6239..1fddbaa212249265cb37b059a5f90fb0fd85c353 100644 (file)
@@ -1,17 +1,20 @@
 {
   "account.block": "Blokkér @{name}",
+  "account.block_domain": "Hide everything from {domain}",
   "account.disclaimer": "Denne brukeren er fra en annen instans. Dette tallet kan være høyere.",
   "account.edit_profile": "Rediger profil",
   "account.follow": "Følg",
   "account.followers": "Følgere",
   "account.follows": "Følger",
   "account.follows_you": "Følger deg",
+  "account.media": "Media",
   "account.mention": "Nevn @{name}",
   "account.mute": "Demp @{name}",
   "account.posts": "Innlegg",
   "account.report": "Rapportér @{name}",
   "account.requested": "Venter på godkjennelse",
   "account.unblock": "Avblokker @{name}",
+  "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "Avfølg",
   "account.unmute": "Avdemp @{name}",
   "boost_modal.combo": "You kan trykke {combo} for å hoppe over dette neste gang",
@@ -40,6 +43,8 @@
   "confirmations.block.message": "Are you sure you want to block {name}?",
   "confirmations.delete.confirm": "Delete",
   "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.domain_block.confirm": "Hide entire domain",
+  "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
   "confirmations.mute.confirm": "Mute",
   "confirmations.mute.message": "Are you sure you want to mute {name}?",
   "emoji_button.activity": "Activity",
index 8d4911187eb8c712b63beed49ef9dacb2bc46d6c..506cf42e5eafadd2589c7140d64b435facad1f03 100644 (file)
@@ -1,17 +1,20 @@
 {
   "account.block": "Blocar",
+  "account.block_domain": "Hide everything from {domain}",
   "account.disclaimer": "Aqueste compte es sus una autra instància. Los nombres pòdon èsser mai grandes.",
   "account.edit_profile": "Modificar lo perfil",
   "account.follow": "Sègre",
   "account.followers": "Abonats",
   "account.follows": "Abonaments",
   "account.follows_you": "Vos sèc",
+  "account.media": "Media",
   "account.mention": "Mencionar",
   "account.mute": "Rescondre",
   "account.posts": "Estatuts",
   "account.report": "Senhalar",
   "account.requested": "Invitacion mandada",
   "account.unblock": "Desblocar",
+  "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "Quitar de sègre",
   "account.unmute": "Quitar de rescondre",
   "boost_modal.combo": "Podètz butar {combo} per passar aquò lo còp que ven",
@@ -40,6 +43,8 @@
   "confirmations.block.message": "Sètz segur de voler blocar {name}?",
   "confirmations.delete.confirm": "Suprimir",
   "confirmations.delete.message": "Sètz segur de voler suprimir l’estatut ?",
+  "confirmations.domain_block.confirm": "Hide entire domain",
+  "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
   "confirmations.mute.confirm": "Metre en silenci",
   "confirmations.mute.message": "Sètz segur de voler metre en silenci {name}?",
   "emoji_button.activity": "Activitat",
index 2a18dff8f1b07cbdeb80222f8315084dc8c08f28..f51d2ee58d9a088e25cdc446b599cd2c105f594a 100644 (file)
@@ -1,17 +1,20 @@
 {
   "account.block": "Blokuj @{name}",
+  "account.block_domain": "Hide everything from {domain}",
   "account.disclaimer": "Ten użytkownik pochodzi z innej instancji. Ta liczba może być większa.",
   "account.edit_profile": "Edytuj profil",
   "account.follow": "Obserwuj",
   "account.followers": "Obserwujący",
   "account.follows": "Obserwacje",
   "account.follows_you": "Obserwują cię",
+  "account.media": "Media",
   "account.mention": "Wspomnij o @{name}",
   "account.mute": "Wycisz @{name}",
   "account.posts": "Posty",
   "account.report": "Zgłoś @{name}",
   "account.requested": "Oczekująca prośba",
   "account.unblock": "Odblokuj @{name}",
+  "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "Przestań obserwować",
   "account.unmute": "Cofnij wyciszenie @{name}",
   "boost_modal.combo": "Naciśnij {combo}, aby pominąć to następnym razem",
@@ -40,6 +43,8 @@
   "confirmations.block.message": "Czy na pewno chcesz zablokować {name}?",
   "confirmations.delete.confirm": "Usuń",
   "confirmations.delete.message": "Czy na pewno chcesz usunąć ten status?",
+  "confirmations.domain_block.confirm": "Hide entire domain",
+  "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
   "confirmations.mute.confirm": "Wycisz",
   "confirmations.mute.message": "Czy na pewno chcesz wyciszyć {name}?",
   "emoji_button.activity": "Aktywność",
index 57b31733d23fa729b8c6bdf3d6dab8a17f1b5aaa..c5af75d9c0ad297e770e6dd0c5595e6247941e0e 100644 (file)
@@ -1,17 +1,20 @@
 {
   "account.block": "Bloquear @{name}",
+  "account.block_domain": "Hide everything from {domain}",
   "account.disclaimer": "Essa conta está localizado em outra instância. Os nomes podem ser maiores.",
   "account.edit_profile": "Editar perfil",
   "account.follow": "Seguir",
   "account.followers": "Seguidores",
   "account.follows": "Segue",
   "account.follows_you": "É teu seguidor",
+  "account.media": "Media",
   "account.mention": "Mencionar @{name}",
   "account.mute": "Silenciar @{name}",
   "account.posts": "Posts",
   "account.report": "Denunciar @{name}",
   "account.requested": "A aguardar aprovação",
   "account.unblock": "Não bloquear @{name}",
+  "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "Deixar de seguir",
   "account.unmute": "Não silenciar @{name}",
   "boost_modal.combo": "Pode clicar {combo} para não voltar a ver",
@@ -40,6 +43,8 @@
   "confirmations.block.message": "Are you sure you want to block {name}?",
   "confirmations.delete.confirm": "Delete",
   "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.domain_block.confirm": "Hide entire domain",
+  "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
   "confirmations.mute.confirm": "Mute",
   "confirmations.mute.message": "Are you sure you want to mute {name}?",
   "emoji_button.activity": "Activity",
index 57b31733d23fa729b8c6bdf3d6dab8a17f1b5aaa..c5af75d9c0ad297e770e6dd0c5595e6247941e0e 100644 (file)
@@ -1,17 +1,20 @@
 {
   "account.block": "Bloquear @{name}",
+  "account.block_domain": "Hide everything from {domain}",
   "account.disclaimer": "Essa conta está localizado em outra instância. Os nomes podem ser maiores.",
   "account.edit_profile": "Editar perfil",
   "account.follow": "Seguir",
   "account.followers": "Seguidores",
   "account.follows": "Segue",
   "account.follows_you": "É teu seguidor",
+  "account.media": "Media",
   "account.mention": "Mencionar @{name}",
   "account.mute": "Silenciar @{name}",
   "account.posts": "Posts",
   "account.report": "Denunciar @{name}",
   "account.requested": "A aguardar aprovação",
   "account.unblock": "Não bloquear @{name}",
+  "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "Deixar de seguir",
   "account.unmute": "Não silenciar @{name}",
   "boost_modal.combo": "Pode clicar {combo} para não voltar a ver",
@@ -40,6 +43,8 @@
   "confirmations.block.message": "Are you sure you want to block {name}?",
   "confirmations.delete.confirm": "Delete",
   "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.domain_block.confirm": "Hide entire domain",
+  "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
   "confirmations.mute.confirm": "Mute",
   "confirmations.mute.message": "Are you sure you want to mute {name}?",
   "emoji_button.activity": "Activity",
index 83d74c61952dcd02675ae3791cd869a1c8355cc3..ba4642ab99c256156196f58cb8030f5d8a59181d 100644 (file)
@@ -1,17 +1,20 @@
 {
   "account.block": "Блокировать",
+  "account.block_domain": "Hide everything from {domain}",
   "account.disclaimer": "Это пользователь с другого узла. Число может быть больше.",
   "account.edit_profile": "Изменить профиль",
   "account.follow": "Подписаться",
   "account.followers": "Подписаны",
   "account.follows": "Подписки",
   "account.follows_you": "Подписан(а) на Вас",
+  "account.media": "Media",
   "account.mention": "Упомянуть",
   "account.mute": "Заглушить",
   "account.posts": "Посты",
   "account.report": "Пожаловаться",
   "account.requested": "Ожидает подтверждения",
   "account.unblock": "Разблокировать",
+  "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "Отписаться",
   "account.unmute": "Снять глушение",
   "boost_modal.combo": "Нажмите {combo}, чтобы пропустить это в следующий раз",
@@ -40,6 +43,8 @@
   "confirmations.block.message": "Вы уверены, что хотите заблокировать {name}?",
   "confirmations.delete.confirm": "Удалить",
   "confirmations.delete.message": "Вы уверены, что хотите удалить этот статус?",
+  "confirmations.domain_block.confirm": "Hide entire domain",
+  "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
   "confirmations.mute.confirm": "Заглушить",
   "confirmations.mute.message": "Вы уверены, что хотите заглушить {name}?",
   "emoji_button.activity": "Занятия",
index ef9c43d0b72400d0d8c1d8440a5cd7b3c904d8dd..ebfa95b845fca65588d1992149e46fafce90851f 100644 (file)
@@ -1,17 +1,20 @@
 {
   "account.block": "Engelle @{name}",
+  "account.block_domain": "Hide everything from {domain}",
   "account.disclaimer": "Bu kullanıcının hesabı farklı sunucuda bulunduğu için bu sayı daha fazla olabilir.",
   "account.edit_profile": "Profili düzenle",
   "account.follow": "Takip et",
   "account.followers": "Takipçiler",
   "account.follows": "Takip ettikleri",
   "account.follows_you": "Seni takip ediyor",
+  "account.media": "Media",
   "account.mention": "Bahset @{name}",
   "account.mute": "Sustur @{name}",
   "account.posts": "Gönderiler",
   "account.report": "Rapor et @{name}",
   "account.requested": "Onay bekleniyor",
   "account.unblock": "Engeli kaldır @{name}",
+  "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "Takipten vazgeç",
   "account.unmute": "Sesi aç @{name}",
   "boost_modal.combo": "Bir dahaki sefere {combo} tuşuna basabilirsiniz",
@@ -40,6 +43,8 @@
   "confirmations.block.message": "{name} kullanıcısını engellemek istiyor musunuz?",
   "confirmations.delete.confirm": "Sil",
   "confirmations.delete.message": "Bu gönderiyi silmek istiyor musunuz?",
+  "confirmations.domain_block.confirm": "Hide entire domain",
+  "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
   "confirmations.mute.confirm": "Sessize al",
   "confirmations.mute.message": "{name} kullanıcısını sessize almak istiyor musunuz?",
   "emoji_button.activity": "Aktivite",
index 99df3b4f4e3eafd9de7de728f7f1842fd62a5bc2..cd2fa6b11b608ad935a4b86ba0d7759ce1761b0a 100644 (file)
@@ -1,17 +1,20 @@
 {
   "account.block": "Заблокувати",
+  "account.block_domain": "Hide everything from {domain}",
   "account.disclaimer": "Це користувач з іншої інстанції. Число може бути більше.",
   "account.edit_profile": "Налаштування профілю",
   "account.follow": "Підписатися",
   "account.followers": "Підписники",
   "account.follows": "Підписки",
   "account.follows_you": "Підписаний(-а) на Вас",
+  "account.media": "Media",
   "account.mention": "Згадати",
   "account.mute": "Заглушити",
   "account.posts": "Пости",
   "account.report": "Поскаржитися",
   "account.requested": "Очікує підтвердження",
   "account.unblock": "Розблокувати",
+  "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "Відписатися",
   "account.unmute": "Зняти глушення",
   "boost_modal.combo": "Ви можете натиснути {combo}, щоб пропустити це наступного разу",
@@ -40,6 +43,8 @@
   "confirmations.block.message": "Ви впевнені, що хочете заблокувати {name}?",
   "confirmations.delete.confirm": "Видалити",
   "confirmations.delete.message": "Ви впевнені, що хочете видалити цей допис?",
+  "confirmations.domain_block.confirm": "Hide entire domain",
+  "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
   "confirmations.mute.confirm": "Заглушить",
   "confirmations.mute.message": "Ви впевнені, що хочете заглушити {name}?",
   "emoji_button.activity": "Заняття",
index fed311cf7c7afdcde9b627aaff642ce5d98fdeba..45eb663a0e483f9db389f975324e6d3cefd58a24 100644 (file)
@@ -1,17 +1,20 @@
 {
   "account.block": "屏蔽 @{name}",
+  "account.block_domain": "Hide everything from {domain}",
   "account.disclaimer": "由于这个账户处于另一个服务站上,实际数字会比这个更多。",
   "account.edit_profile": "修改个人资料",
   "account.follow": "关注",
   "account.followers": "关注者",
   "account.follows": "正关注",
   "account.follows_you": "关注你",
+  "account.media": "Media",
   "account.mention": "提及 @{name}",
   "account.mute": "将 @{name} 静音",
   "account.posts": "嘟文",
   "account.report": "举报 @{name}",
   "account.requested": "等候审批",
   "account.unblock": "解除对 @{name} 的屏蔽",
+  "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "取消关注",
   "account.unmute": "取消 @{name} 的静音",
   "boost_modal.combo": "如你想在下次路过时显示,请按{combo},",
@@ -40,6 +43,8 @@
   "confirmations.block.message": "想好了,真的要屏蔽 {name}?",
   "confirmations.delete.confirm": "删除",
   "confirmations.delete.message": "想好了,真的要删除这条嘟文?",
+  "confirmations.domain_block.confirm": "Hide entire domain",
+  "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
   "confirmations.mute.confirm": "静音",
   "confirmations.mute.message": "想好了,真的要静音 {name}?",
   "emoji_button.activity": "活动",
index f3637bf7404a916e1eeb245b8c6ce3c19093bf6d..23611ae97fb61fe4105da2ab8da9b084834d309d 100644 (file)
@@ -1,17 +1,20 @@
 {
   "account.block": "封鎖 @{name}",
+  "account.block_domain": "Hide everything from {domain}",
   "account.disclaimer": "由於這個用戶在另一個服務站,實際數字會比這個更多。",
   "account.edit_profile": "修改個人資料",
   "account.follow": "關注",
   "account.followers": "關注的人",
   "account.follows": "正在關注",
   "account.follows_you": "關注你",
+  "account.media": "Media",
   "account.mention": "提及 @{name}",
   "account.mute": "將 @{name} 靜音",
   "account.posts": "文章",
   "account.report": "舉報 @{name}",
   "account.requested": "等候審批",
   "account.unblock": "解除對 @{name} 的封鎖",
+  "account.unblock_domain": "Unhide {domain}",
   "account.unfollow": "取消關注",
   "account.unmute": "取消 @{name} 的靜音",
   "boost_modal.combo": "如你想在下次路過這顯示,請按{combo},",
@@ -40,6 +43,8 @@
   "confirmations.block.message": "你確定要封鎖{name}嗎?",
   "confirmations.delete.confirm": "刪除",
   "confirmations.delete.message": "你確定要刪除{name}嗎?",
+  "confirmations.domain_block.confirm": "Hide entire domain",
+  "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
   "confirmations.mute.confirm": "靜音",
   "confirmations.mute.message": "你確定要將{name}靜音嗎?",
   "emoji_button.activity": "活動",
index c65c48b43135b7544d7e00f0963808870fe9b1c4..f25c0b55a6e6bea014e5896378f6bfc9f92818a7 100644 (file)
@@ -7,6 +7,10 @@ import {
   ACCOUNT_UNMUTE_SUCCESS,
   RELATIONSHIPS_FETCH_SUCCESS
 } from '../actions/accounts';
+import {
+  DOMAIN_BLOCK_SUCCESS,
+  DOMAIN_UNBLOCK_SUCCESS
+} from '../actions/domain_blocks';
 import Immutable from 'immutable';
 
 const normalizeRelationship = (state, relationship) => state.set(relationship.id, Immutable.fromJS(relationship));
@@ -32,6 +36,10 @@ export default function relationships(state = initialState, action) {
     return normalizeRelationship(state, action.relationship);
   case RELATIONSHIPS_FETCH_SUCCESS:
     return normalizeRelationships(state, action.relationships);
+  case DOMAIN_BLOCK_SUCCESS:
+    return state.setIn([action.accountId, 'domain_blocking'], true);
+  case DOMAIN_UNBLOCK_SUCCESS:
+    return state.setIn([action.accountId, 'domain_blocking'], false);
   default:
     return state;
   }
index 9241d97205b21e2d04a7753a09b2791a24a984a8..bdd64c01a0c617190d4fd7886ba2863a6fbbf171 100644 (file)
@@ -14,6 +14,7 @@ class AccountDomainBlock < ApplicationRecord
   include Paginable
 
   belongs_to :account, required: true
+  validates :domain, presence: true, uniqueness: { scope: :account_id }
 
   after_create  :remove_blocking_cache
   after_destroy :remove_blocking_cache
index c8006bd0b5b3195813f30accfc8903d1bc1a1c01..0ef7512e2991e08df2204d1704a2b2c83d5eea4c 100644 (file)
@@ -23,6 +23,12 @@ module AccountInteractions
     def requested_map(target_account_ids, account_id)
       follow_mapping(FollowRequest.where(target_account_id: target_account_ids, account_id: account_id), :target_account_id)
     end
+
+    def domain_blocking_map(target_account_ids, account_id)
+      accounts_map    = Account.where(id: target_account_ids).select('id, domain').map { |a| [a.id, a.domain] }.to_h
+      blocked_domains = AccountDomainBlock.where(account_id: account_id, domain: accounts_map.values).pluck(:domain)
+      accounts_map.map { |id, domain| [id, blocked_domains.include?(domain)] }.to_h
+    end
   end
 
   included do
index 70021eb65b4c7460eb7242f29013917781312060..80f33a7c77a5bc90b24f12baf4c98b7506e4730f 100644 (file)
@@ -67,7 +67,8 @@ class Status < ApplicationRecord
   scope :local_only, -> { left_outer_joins(:account).where(accounts: { domain: nil }) }
   scope :excluding_silenced_accounts, -> { left_outer_joins(:account).where(accounts: { silenced: false }) }
   scope :including_silenced_accounts, -> { left_outer_joins(:account).where(accounts: { silenced: true }) }
-  scope :not_excluded_by_account, ->(account) { where.not(account_id: account.excluded_from_timeline_account_ids, accounts: { domain: account.excluded_from_timeline_domains }) }
+  scope :not_excluded_by_account, ->(account) { where.not(account_id: account.excluded_from_timeline_account_ids) }
+  scope :not_domain_blocked_by_account, ->(account) { left_outer_joins(:account).where.not(accounts: { domain: account.excluded_from_timeline_domains }) }
 
   cache_associated :account, :application, :media_attachments, :tags, :stream_entry, mentions: :account, reblog: [:account, :application, :stream_entry, :tags, :media_attachments, mentions: :account], thread: :account
 
@@ -152,13 +153,13 @@ class Status < ApplicationRecord
     def as_public_timeline(account = nil, local_only = false)
       query = timeline_scope(local_only).without_replies
 
-      apply_timeline_filters(query, account)
+      apply_timeline_filters(query, account, local_only)
     end
 
     def as_tag_timeline(tag, account = nil, local_only = false)
       query = timeline_scope(local_only).tagged_with(tag)
 
-      apply_timeline_filters(query, account)
+      apply_timeline_filters(query, account, local_only)
     end
 
     def as_outbox_timeline(account)
@@ -222,16 +223,17 @@ class Status < ApplicationRecord
         .without_reblogs
     end
 
-    def apply_timeline_filters(query, account)
+    def apply_timeline_filters(query, account, local_only)
       if account.nil?
         filter_timeline_default(query)
       else
-        filter_timeline_for_account(query, account)
+        filter_timeline_for_account(query, account, local_only)
       end
     end
 
-    def filter_timeline_for_account(query, account)
+    def filter_timeline_for_account(query, account, local_only)
       query = query.not_excluded_by_account(account)
+      query = query.not_domain_blocked_by_account(account) unless local_only
       query = query.in_allowed_languages(account) if account.allowed_languages.present?
       query.merge(account_silencing_filter(account))
     end
diff --git a/app/services/block_domain_from_account_service.rb b/app/services/block_domain_from_account_service.rb
new file mode 100644 (file)
index 0000000..cae7abc
--- /dev/null
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+class BlockDomainFromAccountService < BaseService
+  def call(account, domain)
+    account.block_domain!(domain)
+    account.passive_relationships.where(account: Account.where(domain: domain)).delete_all
+  end
+end
index 150ffe6b2817cac31efe7667491e4ec37e7d0df2..ce22b650598f5213120b8f4a035b5c512ab3f3ed 100644 (file)
@@ -37,15 +37,15 @@ class NotifyService < BaseService
   end
 
   def blocked?
-    blocked   = @recipient.suspended?                                                                                              # Skip if the recipient account is suspended anyway
-    blocked ||= @recipient.id == @notification.from_account.id                                                                     # Skip for interactions with self
-    blocked ||= @recipient.domain_blocking?(@notification.from_account.domain)                                                     # Skip for domain blocked accounts
-    blocked ||= @recipient.blocking?(@notification.from_account)                                                                   # Skip for blocked accounts
-    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   = @recipient.suspended?                                                                                                # Skip if the recipient account is suspended anyway
+    blocked ||= @recipient.id == @notification.from_account.id                                                                       # Skip for interactions with self
+    blocked ||= @recipient.domain_blocking?(@notification.from_account.domain) && !@recipient.following?(@notification.from_account) # Skip for domain blocked accounts
+    blocked ||= @recipient.blocking?(@notification.from_account)                                                                     # Skip for blocked accounts
+    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 ||= send("blocked_#{@notification.type}?")                                                                               # Type-dependent filters
     blocked
   end
 
index bc8361510c78b825624ee13abd090ab2ca4865f3..e9c01103d58337e2fab447bf0cbcc3f2774d9fa6 100644 (file)
@@ -21,9 +21,9 @@ class ProcessInteractionService < BaseService
 
       case verb(xml)
       when :follow
-        follow!(account, target_account) unless target_account.locked? || target_account.blocking?(account)
+        follow!(account, target_account) unless target_account.locked? || target_account.blocking?(account) || target_account.domain_blocking?(account.domain)
       when :request_friend
-        follow_request!(account, target_account) unless !target_account.locked? || target_account.blocking?(account)
+        follow_request!(account, target_account) unless !target_account.locked? || target_account.blocking?(account) || target_account.domain_blocking?(account.domain)
       when :authorize
         authorize_follow_request!(account, target_account)
       when :reject
index 99113eecaf284d15cbb56ca64c9919de91f553f5..504f41c7278ef7e9fb79bbf784c5e998babfd488 100644 (file)
@@ -6,12 +6,22 @@ class SendInteractionService < BaseService
   # @param [Account] source_account
   # @param [Account] target_account
   def call(xml, source_account, target_account)
-    envelope = salmon.pack(xml, source_account.keypair)
-    salmon.post(target_account.salmon_url, envelope)
+    @xml            = xml
+    @source_account = source_account
+    @target_account = target_account
+
+    return if block_notification?
+
+    envelope = salmon.pack(@xml, @source_account.keypair)
+    salmon.post(@target_account.salmon_url, envelope)
   end
 
   private
 
+  def block_notification?
+    DomainBlock.blocked?(@target_account.domain)
+  end
+
   def salmon
     @salmon ||= OStatus2::Salmon.new
   end
index d6f1dd48ab6bebcb0e547202c42e6ed71fd95d99..4f7763d9d0eff3b1647bbe37f5fc04e0a6d98049 100644 (file)
@@ -1,8 +1,9 @@
 object @account
 
 attribute :id
-node(:following)   { |account| @following[account.id]   || false }
-node(:followed_by) { |account| @followed_by[account.id] || false }
-node(:blocking)    { |account| @blocking[account.id]    || false }
-node(:muting)      { |account| @muting[account.id]      || false }
-node(:requested)   { |account| @requested[account.id]   || false }
+node(:following)       { |account| @following[account.id]       || false }
+node(:followed_by)     { |account| @followed_by[account.id]     || false }
+node(:blocking)        { |account| @blocking[account.id]        || false }
+node(:muting)          { |account| @muting[account.id]          || false }
+node(:requested)       { |account| @requested[account.id]       || false }
+node(:domain_blocking) { |account| @domain_blocking[account.id] || false }
diff --git a/spec/services/block_domain_from_account_service_spec.rb b/spec/services/block_domain_from_account_service_spec.rb
new file mode 100644 (file)
index 0000000..e7ee343
--- /dev/null
@@ -0,0 +1,19 @@
+require 'rails_helper'
+
+RSpec.describe BlockDomainFromAccountService do
+  let!(:wolf) { Fabricate(:account, username: 'wolf', domain: 'evil.org') }
+  let!(:alice) { Fabricate(:account, username: 'alice') }
+
+  subject { BlockDomainFromAccountService.new }
+
+  it 'creates domain block' do
+    subject.call(alice, 'evil.org')
+    expect(alice.domain_blocking?('evil.org')).to be true
+  end
+
+  it 'purge followers from blocked domain' do
+    wolf.follow!(alice)
+    subject.call(alice, 'evil.org')
+    expect(wolf.following?(alice)).to be false
+  end
+end
index 29bd741aa6e3361b9bf4b75f4cf723d2ba1e85d2..7a66bd0fe4dc61932fdc4cc056d9200e0c627ce6 100644 (file)
@@ -22,6 +22,12 @@ RSpec.describe NotifyService do
     is_expected.to_not change(Notification, :count)
   end
 
+  it 'does still notify when sender\'s domain is blocked but sender is followed' do
+    recipient.block_domain!(sender.domain)
+    recipient.follow!(sender)
+    is_expected.to 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)