about summary refs log tree commit diff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/javascript/mastodon/components/status_action_bar.js2
-rw-r--r--app/javascript/mastodon/containers/status_container.js9
-rw-r--r--app/javascript/mastodon/features/account/components/header.js2
-rw-r--r--app/javascript/mastodon/features/account_timeline/containers/header_container.js6
-rw-r--r--app/javascript/mastodon/features/status/components/action_bar.js2
-rw-r--r--app/javascript/mastodon/features/status/containers/detailed_status_container.js9
-rw-r--r--app/javascript/mastodon/features/status/index.js9
-rw-r--r--app/javascript/mastodon/features/ui/components/confirmation_modal.js12
-rw-r--r--app/javascript/styles/mastodon/components.scss1
-rw-r--r--app/validators/html_validator.rb10
10 files changed, 52 insertions, 10 deletions
diff --git a/app/javascript/mastodon/components/status_action_bar.js b/app/javascript/mastodon/components/status_action_bar.js
index 53d17d418..0bfbd8879 100644
--- a/app/javascript/mastodon/components/status_action_bar.js
+++ b/app/javascript/mastodon/components/status_action_bar.js
@@ -139,7 +139,7 @@ class StatusActionBar extends ImmutablePureComponent {
   }
 
   handleBlockClick = () => {
-    this.props.onBlock(this.props.status.get('account'));
+    this.props.onBlock(this.props.status);
   }
 
   handleOpen = () => {
diff --git a/app/javascript/mastodon/containers/status_container.js b/app/javascript/mastodon/containers/status_container.js
index b3555c76e..0fce674e2 100644
--- a/app/javascript/mastodon/containers/status_container.js
+++ b/app/javascript/mastodon/containers/status_container.js
@@ -38,6 +38,7 @@ const messages = defineMessages({
   blockConfirm: { id: 'confirmations.block.confirm', defaultMessage: 'Block' },
   replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
   replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
+  blockAndReport: { id: 'confirmations.block.block_and_report', defaultMessage: 'Block & Report' },
 });
 
 const makeMapStateToProps = () => {
@@ -134,11 +135,17 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
     dispatch(openModal('VIDEO', { media, time }));
   },
 
-  onBlock (account) {
+  onBlock (status) {
+    const account = status.get('account');
     dispatch(openModal('CONFIRM', {
       message: <FormattedMessage id='confirmations.block.message' defaultMessage='Are you sure you want to block {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
       confirm: intl.formatMessage(messages.blockConfirm),
       onConfirm: () => dispatch(blockAccount(account.get('id'))),
+      secondary: intl.formatMessage(messages.blockAndReport),
+      onSecondary: () => {
+        dispatch(blockAccount(account.get('id')));
+        dispatch(initReport(account, status));
+      },
     }));
   },
 
diff --git a/app/javascript/mastodon/features/account/components/header.js b/app/javascript/mastodon/features/account/components/header.js
index 41e394585..fd7ad5e07 100644
--- a/app/javascript/mastodon/features/account/components/header.js
+++ b/app/javascript/mastodon/features/account/components/header.js
@@ -212,7 +212,7 @@ class Header extends ImmutablePureComponent {
 
         <div className='account__header__bar'>
           <div className='account__header__tabs'>
-            <a className='avatar' href={account.get('url')}>
+            <a className='avatar' href={account.get('url')} target='_blank'>
               <Avatar account={account} size={90} />
             </a>
 
diff --git a/app/javascript/mastodon/features/account_timeline/containers/header_container.js b/app/javascript/mastodon/features/account_timeline/containers/header_container.js
index df742f9ec..a06a0b095 100644
--- a/app/javascript/mastodon/features/account_timeline/containers/header_container.js
+++ b/app/javascript/mastodon/features/account_timeline/containers/header_container.js
@@ -26,6 +26,7 @@ const messages = defineMessages({
   unfollowConfirm: { id: 'confirmations.unfollow.confirm', defaultMessage: 'Unfollow' },
   blockConfirm: { id: 'confirmations.block.confirm', defaultMessage: 'Block' },
   blockDomainConfirm: { id: 'confirmations.domain_block.confirm', defaultMessage: 'Hide entire domain' },
+  blockAndReport: { id: 'confirmations.block.block_and_report', defaultMessage: 'Block & Report' },
 });
 
 const makeMapStateToProps = () => {
@@ -65,6 +66,11 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
         message: <FormattedMessage id='confirmations.block.message' defaultMessage='Are you sure you want to block {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
         confirm: intl.formatMessage(messages.blockConfirm),
         onConfirm: () => dispatch(blockAccount(account.get('id'))),
+        secondary: intl.formatMessage(messages.blockAndReport),
+        onSecondary: () => {
+          dispatch(blockAccount(account.get('id')));
+          dispatch(initReport(account));
+        },
       }));
     }
   },
diff --git a/app/javascript/mastodon/features/status/components/action_bar.js b/app/javascript/mastodon/features/status/components/action_bar.js
index 73be1fc5f..3e511b7a6 100644
--- a/app/javascript/mastodon/features/status/components/action_bar.js
+++ b/app/javascript/mastodon/features/status/components/action_bar.js
@@ -92,7 +92,7 @@ class ActionBar extends React.PureComponent {
   }
 
   handleBlockClick = () => {
-    this.props.onBlock(this.props.status.get('account'));
+    this.props.onBlock(this.props.status);
   }
 
   handleReport = () => {
diff --git a/app/javascript/mastodon/features/status/containers/detailed_status_container.js b/app/javascript/mastodon/features/status/containers/detailed_status_container.js
index 2c0db0a6b..61e0c428a 100644
--- a/app/javascript/mastodon/features/status/containers/detailed_status_container.js
+++ b/app/javascript/mastodon/features/status/containers/detailed_status_container.js
@@ -38,6 +38,7 @@ const messages = defineMessages({
   blockConfirm: { id: 'confirmations.block.confirm', defaultMessage: 'Block' },
   replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
   replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
+  blockAndReport: { id: 'confirmations.block.block_and_report', defaultMessage: 'Block & Report' },
 });
 
 const makeMapStateToProps = () => {
@@ -135,11 +136,17 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
     dispatch(openModal('VIDEO', { media, time }));
   },
 
-  onBlock (account) {
+  onBlock (status) {
+    const account = status.get('account');
     dispatch(openModal('CONFIRM', {
       message: <FormattedMessage id='confirmations.block.message' defaultMessage='Are you sure you want to block {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
       confirm: intl.formatMessage(messages.blockConfirm),
       onConfirm: () => dispatch(blockAccount(account.get('id'))),
+      secondary: intl.formatMessage(messages.blockAndReport),
+      onSecondary: () => {
+        dispatch(blockAccount(account.get('id')));
+        dispatch(initReport(account, status));
+      },
     }));
   },
 
diff --git a/app/javascript/mastodon/features/status/index.js b/app/javascript/mastodon/features/status/index.js
index 3b5febcac..c0ea460e8 100644
--- a/app/javascript/mastodon/features/status/index.js
+++ b/app/javascript/mastodon/features/status/index.js
@@ -57,6 +57,7 @@ const messages = defineMessages({
   detailedStatus: { id: 'status.detailed_status', defaultMessage: 'Detailed conversation view' },
   replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
   replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
+  blockAndReport: { id: 'confirmations.block.block_and_report', defaultMessage: 'Block & Report' },
 });
 
 const makeMapStateToProps = () => {
@@ -253,13 +254,19 @@ class Status extends ImmutablePureComponent {
     }
   }
 
-  handleBlockClick = (account) => {
+  handleBlockClick = (status) => {
     const { dispatch, intl } = this.props;
+    const account = status.get('account');
 
     dispatch(openModal('CONFIRM', {
       message: <FormattedMessage id='confirmations.block.message' defaultMessage='Are you sure you want to block {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
       confirm: intl.formatMessage(messages.blockConfirm),
       onConfirm: () => dispatch(blockAccount(account.get('id'))),
+      secondary: intl.formatMessage(messages.blockAndReport),
+      onSecondary: () => {
+        dispatch(blockAccount(account.get('id')));
+        dispatch(initReport(account, status));
+      },
     }));
   }
 
diff --git a/app/javascript/mastodon/features/ui/components/confirmation_modal.js b/app/javascript/mastodon/features/ui/components/confirmation_modal.js
index f0f3ad134..1227fa453 100644
--- a/app/javascript/mastodon/features/ui/components/confirmation_modal.js
+++ b/app/javascript/mastodon/features/ui/components/confirmation_modal.js
@@ -11,6 +11,8 @@ class ConfirmationModal extends React.PureComponent {
     confirm: PropTypes.string.isRequired,
     onClose: PropTypes.func.isRequired,
     onConfirm: PropTypes.func.isRequired,
+    secondary: PropTypes.string,
+    onSecondary: PropTypes.func,
     intl: PropTypes.object.isRequired,
   };
 
@@ -23,6 +25,11 @@ class ConfirmationModal extends React.PureComponent {
     this.props.onConfirm();
   }
 
+  handleSecondary = () => {
+    this.props.onClose();
+    this.props.onSecondary();
+  }
+
   handleCancel = () => {
     this.props.onClose();
   }
@@ -32,7 +39,7 @@ class ConfirmationModal extends React.PureComponent {
   }
 
   render () {
-    const { message, confirm } = this.props;
+    const { message, confirm, secondary } = this.props;
 
     return (
       <div className='modal-root__modal confirmation-modal'>
@@ -44,6 +51,9 @@ class ConfirmationModal extends React.PureComponent {
           <Button onClick={this.handleCancel} className='confirmation-modal__cancel-button'>
             <FormattedMessage id='confirmation_modal.cancel' defaultMessage='Cancel' />
           </Button>
+          {secondary !== undefined && (
+            <Button text={secondary} onClick={this.handleSecondary} className='confirmation-modal__secondary-button' />
+          )}
           <Button text={confirm} onClick={this.handleClick} ref={this.setRef} />
         </div>
       </div>
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index e26341149..53c392062 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -4128,6 +4128,7 @@ a.status-card.compact:hover {
 
 .confirmation-modal__action-bar,
 .mute-modal__action-bar {
+  .confirmation-modal__secondary-button,
   .confirmation-modal__cancel-button,
   .mute-modal__cancel-button {
     background-color: transparent;
diff --git a/app/validators/html_validator.rb b/app/validators/html_validator.rb
index 882c35d41..b7caee5a9 100644
--- a/app/validators/html_validator.rb
+++ b/app/validators/html_validator.rb
@@ -3,12 +3,16 @@
 class HtmlValidator < ActiveModel::EachValidator
   def validate_each(record, attribute, value)
     return if value.blank?
-    record.errors.add(attribute, I18n.t('html_validator.invalid_markup')) unless valid_html?(value)
+    errors = html_errors(value)
+    unless errors.empty?
+      record.errors.add(attribute, I18n.t('html_validator.invalid_markup', error: errors.first.to_s))
+    end
   end
 
   private
 
-  def valid_html?(str)
-    Nokogiri::HTML.fragment(str).to_s == str
+  def html_errors(str)
+    fragment = Nokogiri::HTML.fragment(str)
+    fragment.errors
   end
 end