Merge branch 'main' into glitch-soc/merge-upstream
This commit is contained in:
		
						commit
						9dd62c95c5
					
				
					 15 changed files with 954 additions and 232 deletions
				
			
		| 
						 | 
					@ -1,89 +1,38 @@
 | 
				
			||||||
import api from '../api';
 | 
					import api from '../api';
 | 
				
			||||||
import { openModal, closeModal } from './modal';
 | 
					import { openModal } from './modal';
 | 
				
			||||||
 | 
					 | 
				
			||||||
export const REPORT_INIT   = 'REPORT_INIT';
 | 
					 | 
				
			||||||
export const REPORT_CANCEL = 'REPORT_CANCEL';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const REPORT_SUBMIT_REQUEST = 'REPORT_SUBMIT_REQUEST';
 | 
					export const REPORT_SUBMIT_REQUEST = 'REPORT_SUBMIT_REQUEST';
 | 
				
			||||||
export const REPORT_SUBMIT_SUCCESS = 'REPORT_SUBMIT_SUCCESS';
 | 
					export const REPORT_SUBMIT_SUCCESS = 'REPORT_SUBMIT_SUCCESS';
 | 
				
			||||||
export const REPORT_SUBMIT_FAIL    = 'REPORT_SUBMIT_FAIL';
 | 
					export const REPORT_SUBMIT_FAIL    = 'REPORT_SUBMIT_FAIL';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const REPORT_STATUS_TOGGLE  = 'REPORT_STATUS_TOGGLE';
 | 
					export const initReport = (account, status) => dispatch =>
 | 
				
			||||||
export const REPORT_COMMENT_CHANGE = 'REPORT_COMMENT_CHANGE';
 | 
					  dispatch(openModal('REPORT', {
 | 
				
			||||||
export const REPORT_FORWARD_CHANGE = 'REPORT_FORWARD_CHANGE';
 | 
					    accountId: account.get('id'),
 | 
				
			||||||
 | 
					    statusId: status.get('id'),
 | 
				
			||||||
 | 
					  }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function initReport(account, status) {
 | 
					export const submitReport = (params, onSuccess, onFail) => (dispatch, getState) => {
 | 
				
			||||||
  return dispatch => {
 | 
					  dispatch(submitReportRequest());
 | 
				
			||||||
    dispatch({
 | 
					 | 
				
			||||||
      type: REPORT_INIT,
 | 
					 | 
				
			||||||
      account,
 | 
					 | 
				
			||||||
      status,
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    dispatch(openModal('REPORT'));
 | 
					  api(getState).post('/api/v1/reports', params).then(response => {
 | 
				
			||||||
  };
 | 
					    dispatch(submitReportSuccess(response.data));
 | 
				
			||||||
 | 
					    if (onSuccess) onSuccess();
 | 
				
			||||||
 | 
					  }).catch(error => {
 | 
				
			||||||
 | 
					    dispatch(submitReportFail(error));
 | 
				
			||||||
 | 
					    if (onFail) onFail();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function cancelReport() {
 | 
					export const submitReportRequest = () => ({
 | 
				
			||||||
  return {
 | 
					  type: REPORT_SUBMIT_REQUEST,
 | 
				
			||||||
    type: REPORT_CANCEL,
 | 
					});
 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function toggleStatusReport(statusId, checked) {
 | 
					export const submitReportSuccess = report => ({
 | 
				
			||||||
  return {
 | 
					  type: REPORT_SUBMIT_SUCCESS,
 | 
				
			||||||
    type: REPORT_STATUS_TOGGLE,
 | 
					  report,
 | 
				
			||||||
    statusId,
 | 
					});
 | 
				
			||||||
    checked,
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function submitReport() {
 | 
					export const submitReportFail = error => ({
 | 
				
			||||||
  return (dispatch, getState) => {
 | 
					  type: REPORT_SUBMIT_FAIL,
 | 
				
			||||||
    dispatch(submitReportRequest());
 | 
					  error,
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
    api(getState).post('/api/v1/reports', {
 | 
					 | 
				
			||||||
      account_id: getState().getIn(['reports', 'new', 'account_id']),
 | 
					 | 
				
			||||||
      status_ids: getState().getIn(['reports', 'new', 'status_ids']),
 | 
					 | 
				
			||||||
      comment: getState().getIn(['reports', 'new', 'comment']),
 | 
					 | 
				
			||||||
      forward: getState().getIn(['reports', 'new', 'forward']),
 | 
					 | 
				
			||||||
    }).then(response => {
 | 
					 | 
				
			||||||
      dispatch(closeModal());
 | 
					 | 
				
			||||||
      dispatch(submitReportSuccess(response.data));
 | 
					 | 
				
			||||||
    }).catch(error => dispatch(submitReportFail(error)));
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function submitReportRequest() {
 | 
					 | 
				
			||||||
  return {
 | 
					 | 
				
			||||||
    type: REPORT_SUBMIT_REQUEST,
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function submitReportSuccess(report) {
 | 
					 | 
				
			||||||
  return {
 | 
					 | 
				
			||||||
    type: REPORT_SUBMIT_SUCCESS,
 | 
					 | 
				
			||||||
    report,
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function submitReportFail(error) {
 | 
					 | 
				
			||||||
  return {
 | 
					 | 
				
			||||||
    type: REPORT_SUBMIT_FAIL,
 | 
					 | 
				
			||||||
    error,
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function changeReportComment(comment) {
 | 
					 | 
				
			||||||
  return {
 | 
					 | 
				
			||||||
    type: REPORT_COMMENT_CHANGE,
 | 
					 | 
				
			||||||
    comment,
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function changeReportForward(forward) {
 | 
					 | 
				
			||||||
  return {
 | 
					 | 
				
			||||||
    type: REPORT_FORWARD_CHANGE,
 | 
					 | 
				
			||||||
    forward,
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										27
									
								
								app/javascript/mastodon/actions/rules.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								app/javascript/mastodon/actions/rules.js
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,27 @@
 | 
				
			||||||
 | 
					import api from '../api';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const RULES_FETCH_REQUEST = 'RULES_FETCH_REQUEST';
 | 
				
			||||||
 | 
					export const RULES_FETCH_SUCCESS = 'RULES_FETCH_SUCCESS';
 | 
				
			||||||
 | 
					export const RULES_FETCH_FAIL    = 'RULES_FETCH_FAIL';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const fetchRules = () => (dispatch, getState) => {
 | 
				
			||||||
 | 
					  dispatch(fetchRulesRequest());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  api(getState)
 | 
				
			||||||
 | 
					    .get('/api/v1/instance').then(({ data }) => dispatch(fetchRulesSuccess(data.rules)))
 | 
				
			||||||
 | 
					    .catch(err => dispatch(fetchRulesFail(err)));
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const fetchRulesRequest = () => ({
 | 
				
			||||||
 | 
					  type: RULES_FETCH_REQUEST,
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const fetchRulesSuccess = rules => ({
 | 
				
			||||||
 | 
					  type: RULES_FETCH_SUCCESS,
 | 
				
			||||||
 | 
					  rules,
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const fetchRulesFail = error => ({
 | 
				
			||||||
 | 
					  type: RULES_FETCH_FAIL,
 | 
				
			||||||
 | 
					  error,
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										9
									
								
								app/javascript/mastodon/components/check.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								app/javascript/mastodon/components/check.js
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,9 @@
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Check = () => (
 | 
				
			||||||
 | 
					  <svg width='14' height='11' viewBox='0 0 14 11'>
 | 
				
			||||||
 | 
					    <path d='M11.264 0L5.26 6.004 2.103 2.847 0 4.95l5.26 5.26 8.108-8.107L11.264 0' fill='currentColor' fillRule='evenodd' />
 | 
				
			||||||
 | 
					  </svg>
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Check;
 | 
				
			||||||
							
								
								
									
										93
									
								
								app/javascript/mastodon/features/report/category.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								app/javascript/mastodon/features/report/category.js
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,93 @@
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
 | 
					import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 | 
				
			||||||
 | 
					import Button from 'mastodon/components/button';
 | 
				
			||||||
 | 
					import Option from './components/option';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const messages = defineMessages({
 | 
				
			||||||
 | 
					  dislike: { id: 'report.reasons.dislike', defaultMessage: 'I don\'t like it' },
 | 
				
			||||||
 | 
					  dislike_description: { id: 'report.reasons.dislike_description', defaultMessage: 'It is not something you want to see' },
 | 
				
			||||||
 | 
					  spam: { id: 'report.reasons.spam', defaultMessage: 'It\'s spam' },
 | 
				
			||||||
 | 
					  spam_description: { id: 'report.reasons.spam_description', defaultMessage: 'Malicious links, fake engagement, or repetetive replies' },
 | 
				
			||||||
 | 
					  violation: { id: 'report.reasons.violation', defaultMessage: 'It violates server rules' },
 | 
				
			||||||
 | 
					  violation_description: { id: 'report.reasons.violation_description', defaultMessage: 'You are aware that it breaks specific rules' },
 | 
				
			||||||
 | 
					  other: { id: 'report.reasons.other', defaultMessage: 'It\'s something else' },
 | 
				
			||||||
 | 
					  other_description: { id: 'report.reasons.other_description', defaultMessage: 'The issue does not fit into other categories' },
 | 
				
			||||||
 | 
					  status: { id: 'report.category.title_status', defaultMessage: 'post' },
 | 
				
			||||||
 | 
					  account: { id: 'report.category.title_account', defaultMessage: 'profile' },
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default @injectIntl
 | 
				
			||||||
 | 
					class Category extends React.PureComponent {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static propTypes = {
 | 
				
			||||||
 | 
					    onNextStep: PropTypes.func.isRequired,
 | 
				
			||||||
 | 
					    category: PropTypes.string,
 | 
				
			||||||
 | 
					    onChangeCategory: PropTypes.func.isRequired,
 | 
				
			||||||
 | 
					    startedFrom: PropTypes.oneOf(['status', 'account']),
 | 
				
			||||||
 | 
					    intl: PropTypes.object.isRequired,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleNextClick = () => {
 | 
				
			||||||
 | 
					    const { onNextStep, category } = this.props;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch(category) {
 | 
				
			||||||
 | 
					    case 'dislike':
 | 
				
			||||||
 | 
					      onNextStep('thanks');
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    case 'violation':
 | 
				
			||||||
 | 
					      onNextStep('rules');
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					      onNextStep('statuses');
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleCategoryToggle = (value, checked) => {
 | 
				
			||||||
 | 
					    const { onChangeCategory } = this.props;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (checked) {
 | 
				
			||||||
 | 
					      onChangeCategory(value);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render () {
 | 
				
			||||||
 | 
					    const { category, startedFrom, intl } = this.props;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const options = [
 | 
				
			||||||
 | 
					      'dislike',
 | 
				
			||||||
 | 
					      'spam',
 | 
				
			||||||
 | 
					      'violation',
 | 
				
			||||||
 | 
					      'other',
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <React.Fragment>
 | 
				
			||||||
 | 
					        <h3 className='report-dialog-modal__title'><FormattedMessage id='report.category.title' defaultMessage="Tell us what's going on with this {type}" values={{ type: intl.formatMessage(messages[startedFrom]) }} /></h3>
 | 
				
			||||||
 | 
					        <p className='report-dialog-modal__lead'><FormattedMessage id='report.category.subtitle' defaultMessage='Choose the best match' /></p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <div>
 | 
				
			||||||
 | 
					          {options.map(item => (
 | 
				
			||||||
 | 
					            <Option
 | 
				
			||||||
 | 
					              key={item}
 | 
				
			||||||
 | 
					              name='category'
 | 
				
			||||||
 | 
					              value={item}
 | 
				
			||||||
 | 
					              checked={category === item}
 | 
				
			||||||
 | 
					              onToggle={this.handleCategoryToggle}
 | 
				
			||||||
 | 
					              label={intl.formatMessage(messages[item])}
 | 
				
			||||||
 | 
					              description={intl.formatMessage(messages[`${item}_description`])}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					          ))}
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <div className='flex-spacer' />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <div className='report-dialog-modal__actions'>
 | 
				
			||||||
 | 
					          <Button onClick={this.handleNextClick} disabled={category === null}><FormattedMessage id='report.next' defaultMessage='Next' /></Button>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </React.Fragment>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										83
									
								
								app/javascript/mastodon/features/report/comment.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								app/javascript/mastodon/features/report/comment.js
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,83 @@
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
 | 
					import { injectIntl, defineMessages, FormattedMessage } from 'react-intl';
 | 
				
			||||||
 | 
					import Button from 'mastodon/components/button';
 | 
				
			||||||
 | 
					import Toggle from 'react-toggle';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const messages = defineMessages({
 | 
				
			||||||
 | 
					  placeholder: { id: 'report.placeholder', defaultMessage: 'Type or paste additional comments' },
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default @injectIntl
 | 
				
			||||||
 | 
					class Comment extends React.PureComponent {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static propTypes = {
 | 
				
			||||||
 | 
					    onSubmit: PropTypes.func.isRequired,
 | 
				
			||||||
 | 
					    comment: PropTypes.string.isRequired,
 | 
				
			||||||
 | 
					    onChangeComment: PropTypes.func.isRequired,
 | 
				
			||||||
 | 
					    intl: PropTypes.object.isRequired,
 | 
				
			||||||
 | 
					    isSubmitting: PropTypes.bool,
 | 
				
			||||||
 | 
					    forward: PropTypes.bool,
 | 
				
			||||||
 | 
					    isRemote: PropTypes.bool,
 | 
				
			||||||
 | 
					    domain: PropTypes.string,
 | 
				
			||||||
 | 
					    onChangeForward: PropTypes.func.isRequired,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleClick = () => {
 | 
				
			||||||
 | 
					    const { onSubmit } = this.props;
 | 
				
			||||||
 | 
					    onSubmit();
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleChange = e => {
 | 
				
			||||||
 | 
					    const { onChangeComment } = this.props;
 | 
				
			||||||
 | 
					    onChangeComment(e.target.value);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleKeyDown = e => {
 | 
				
			||||||
 | 
					    if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
 | 
				
			||||||
 | 
					      this.handleClick();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleForwardChange = e => {
 | 
				
			||||||
 | 
					    const { onChangeForward } = this.props;
 | 
				
			||||||
 | 
					    onChangeForward(e.target.checked);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render () {
 | 
				
			||||||
 | 
					    const { comment, isRemote, forward, domain, isSubmitting, intl } = this.props;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <React.Fragment>
 | 
				
			||||||
 | 
					        <h3 className='report-dialog-modal__title'><FormattedMessage id='report.comment.title' defaultMessage='Is there anything else you think we should know?' /></h3>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <textarea
 | 
				
			||||||
 | 
					          className='report-dialog-modal__textarea'
 | 
				
			||||||
 | 
					          placeholder={intl.formatMessage(messages.placeholder)}
 | 
				
			||||||
 | 
					          value={comment}
 | 
				
			||||||
 | 
					          onChange={this.handleChange}
 | 
				
			||||||
 | 
					          onKeyDown={this.handleKeyDown}
 | 
				
			||||||
 | 
					          disabled={isSubmitting}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        {isRemote && (
 | 
				
			||||||
 | 
					          <React.Fragment>
 | 
				
			||||||
 | 
					            <p className='report-dialog-modal__lead'><FormattedMessage id='report.forward_hint' defaultMessage='The account is from another server. Send an anonymized copy of the report there as well?' /></p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <label className='report-dialog-modal__toggle'>
 | 
				
			||||||
 | 
					              <Toggle checked={forward} disabled={isSubmitting} onChange={this.handleForwardChange} />
 | 
				
			||||||
 | 
					              <FormattedMessage id='report.forward' defaultMessage='Forward to {target}' values={{ target: domain }} />
 | 
				
			||||||
 | 
					            </label>
 | 
				
			||||||
 | 
					          </React.Fragment>
 | 
				
			||||||
 | 
					        )}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <div className='flex-spacer' />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <div className='report-dialog-modal__actions'>
 | 
				
			||||||
 | 
					          <Button onClick={this.handleClick}><FormattedMessage id='report.submit' defaultMessage='Submit report' /></Button>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </React.Fragment>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										60
									
								
								app/javascript/mastodon/features/report/components/option.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								app/javascript/mastodon/features/report/components/option.js
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,60 @@
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
 | 
					import classNames from 'classnames';
 | 
				
			||||||
 | 
					import Check from 'mastodon/components/check';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class Option extends React.PureComponent {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static propTypes = {
 | 
				
			||||||
 | 
					    name: PropTypes.string.isRequired,
 | 
				
			||||||
 | 
					    value: PropTypes.string.isRequired,
 | 
				
			||||||
 | 
					    checked: PropTypes.bool,
 | 
				
			||||||
 | 
					    label: PropTypes.node,
 | 
				
			||||||
 | 
					    description: PropTypes.node,
 | 
				
			||||||
 | 
					    onToggle: PropTypes.func,
 | 
				
			||||||
 | 
					    multiple: PropTypes.bool,
 | 
				
			||||||
 | 
					    labelComponent: PropTypes.node,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleKeyPress = e => {
 | 
				
			||||||
 | 
					    const { value, checked, onToggle } = this.props;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (e.key === 'Enter' || e.key === ' ') {
 | 
				
			||||||
 | 
					      e.stopPropagation();
 | 
				
			||||||
 | 
					      e.preventDefault();
 | 
				
			||||||
 | 
					      onToggle(value, !checked);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleChange = e => {
 | 
				
			||||||
 | 
					    const { value, onToggle } = this.props;
 | 
				
			||||||
 | 
					    onToggle(value, e.target.checked);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render () {
 | 
				
			||||||
 | 
					    const { name, value, checked, label, labelComponent, description, multiple } = this.props;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <label className='dialog-option poll__option selectable'>
 | 
				
			||||||
 | 
					        <input type={multiple ? 'checkbox' : 'radio'} name={name} value={value} checked={checked} onChange={this.handleChange} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <span
 | 
				
			||||||
 | 
					          className={classNames('poll__input', { active: checked, checkbox: multiple })}
 | 
				
			||||||
 | 
					          tabIndex='0'
 | 
				
			||||||
 | 
					          role='radio'
 | 
				
			||||||
 | 
					          onKeyPress={this.handleKeyPress}
 | 
				
			||||||
 | 
					          aria-checked={checked}
 | 
				
			||||||
 | 
					          aria-label={label}
 | 
				
			||||||
 | 
					        >{checked && <Check />}</span>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        {labelComponent ? labelComponent : (
 | 
				
			||||||
 | 
					          <span className='poll__option__text'>
 | 
				
			||||||
 | 
					            <strong>{label}</strong>
 | 
				
			||||||
 | 
					            {description}
 | 
				
			||||||
 | 
					          </span>
 | 
				
			||||||
 | 
					        )}
 | 
				
			||||||
 | 
					      </label>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,23 +1,32 @@
 | 
				
			||||||
import React from 'react';
 | 
					import React from 'react';
 | 
				
			||||||
import PropTypes from 'prop-types';
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
 | 
					import ImmutablePropTypes from 'react-immutable-proptypes';
 | 
				
			||||||
import Toggle from 'react-toggle';
 | 
					 | 
				
			||||||
import noop from 'lodash/noop';
 | 
					import noop from 'lodash/noop';
 | 
				
			||||||
import StatusContent from '../../../components/status_content';
 | 
					import StatusContent from 'mastodon/components/status_content';
 | 
				
			||||||
import { MediaGallery, Video } from '../../ui/util/async-components';
 | 
					import { MediaGallery, Video } from 'mastodon/features/ui/util/async-components';
 | 
				
			||||||
import Bundle from '../../ui/components/bundle';
 | 
					import Bundle from 'mastodon/features/ui/components/bundle';
 | 
				
			||||||
 | 
					import Avatar from 'mastodon/components/avatar';
 | 
				
			||||||
 | 
					import DisplayName from 'mastodon/components/display_name';
 | 
				
			||||||
 | 
					import RelativeTimestamp from 'mastodon/components/relative_timestamp';
 | 
				
			||||||
 | 
					import Option from './option';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class StatusCheckBox extends React.PureComponent {
 | 
					export default class StatusCheckBox extends React.PureComponent {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static propTypes = {
 | 
					  static propTypes = {
 | 
				
			||||||
 | 
					    id: PropTypes.string.isRequired,
 | 
				
			||||||
    status: ImmutablePropTypes.map.isRequired,
 | 
					    status: ImmutablePropTypes.map.isRequired,
 | 
				
			||||||
    checked: PropTypes.bool,
 | 
					    checked: PropTypes.bool,
 | 
				
			||||||
    onToggle: PropTypes.func.isRequired,
 | 
					    onToggle: PropTypes.func.isRequired,
 | 
				
			||||||
    disabled: PropTypes.bool,
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleStatusesToggle = (value, checked) => {
 | 
				
			||||||
 | 
					    const { onToggle } = this.props;
 | 
				
			||||||
 | 
					    onToggle(value, checked);
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  render () {
 | 
					  render () {
 | 
				
			||||||
    const { status, checked, onToggle, disabled } = this.props;
 | 
					    const { status, checked } = this.props;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let media = null;
 | 
					    let media = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (status.get('reblog')) {
 | 
					    if (status.get('reblog')) {
 | 
				
			||||||
| 
						 | 
					@ -50,24 +59,46 @@ export default class StatusCheckBox extends React.PureComponent {
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        media = (
 | 
					        media = (
 | 
				
			||||||
          <Bundle fetchComponent={MediaGallery} loading={this.renderLoadingMediaGallery} >
 | 
					          <Bundle fetchComponent={MediaGallery} loading={this.renderLoadingMediaGallery} >
 | 
				
			||||||
            {Component => <Component media={status.get('media_attachments')} sensitive={status.get('sensitive')} height={110} onOpenMedia={noop} />}
 | 
					            {Component => (
 | 
				
			||||||
 | 
					              <Component
 | 
				
			||||||
 | 
					                media={status.get('media_attachments')}
 | 
				
			||||||
 | 
					                sensitive={status.get('sensitive')}
 | 
				
			||||||
 | 
					                height={110}
 | 
				
			||||||
 | 
					                onOpenMedia={noop}
 | 
				
			||||||
 | 
					              />
 | 
				
			||||||
 | 
					            )}
 | 
				
			||||||
          </Bundle>
 | 
					          </Bundle>
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    const labelComponent = (
 | 
				
			||||||
      <div className='status-check-box'>
 | 
					      <div className='status-check-box__status poll__option__text'>
 | 
				
			||||||
        <div className='status-check-box__status'>
 | 
					        <div className='detailed-status__display-name'>
 | 
				
			||||||
          <StatusContent status={status} />
 | 
					          <div className='detailed-status__display-avatar'>
 | 
				
			||||||
          {media}
 | 
					            <Avatar account={status.get('account')} size={46} />
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          <div><DisplayName account={status.get('account')} /> · <RelativeTimestamp timestamp={status.get('created_at')} /></div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <div className='status-check-box-toggle'>
 | 
					        <StatusContent status={status} />
 | 
				
			||||||
          <Toggle checked={checked} onChange={onToggle} disabled={disabled} />
 | 
					
 | 
				
			||||||
        </div>
 | 
					        {media}
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Option
 | 
				
			||||||
 | 
					        name='status_ids'
 | 
				
			||||||
 | 
					        value={status.get('id')}
 | 
				
			||||||
 | 
					        checked={checked}
 | 
				
			||||||
 | 
					        onToggle={this.handleStatusesToggle}
 | 
				
			||||||
 | 
					        label={status.get('search_index')}
 | 
				
			||||||
 | 
					        labelComponent={labelComponent}
 | 
				
			||||||
 | 
					        multiple
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,19 +1,15 @@
 | 
				
			||||||
import { connect } from 'react-redux';
 | 
					import { connect } from 'react-redux';
 | 
				
			||||||
import StatusCheckBox from '../components/status_check_box';
 | 
					import StatusCheckBox from '../components/status_check_box';
 | 
				
			||||||
import { toggleStatusReport } from '../../../actions/reports';
 | 
					import { makeGetStatus } from 'mastodon/selectors';
 | 
				
			||||||
import { Set as ImmutableSet } from 'immutable';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const mapStateToProps = (state, { id }) => ({
 | 
					const makeMapStateToProps = () => {
 | 
				
			||||||
  status: state.getIn(['statuses', id]),
 | 
					  const getStatus = makeGetStatus();
 | 
				
			||||||
  checked: state.getIn(['reports', 'new', 'status_ids'], ImmutableSet()).includes(id),
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const mapDispatchToProps = (dispatch, { id }) => ({
 | 
					  const mapStateToProps = (state, { id }) => ({
 | 
				
			||||||
 | 
					    status: getStatus(state, { id }),
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  onToggle (e) {
 | 
					  return mapStateToProps;
 | 
				
			||||||
    dispatch(toggleStatusReport(id, e.target.checked));
 | 
					};
 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
});
 | 
					export default connect(makeMapStateToProps)(StatusCheckBox);
 | 
				
			||||||
 | 
					 | 
				
			||||||
export default connect(mapStateToProps, mapDispatchToProps)(StatusCheckBox);
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										64
									
								
								app/javascript/mastodon/features/report/rules.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								app/javascript/mastodon/features/report/rules.js
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,64 @@
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
 | 
					import ImmutablePropTypes from 'react-immutable-proptypes';
 | 
				
			||||||
 | 
					import { connect } from 'react-redux';
 | 
				
			||||||
 | 
					import { FormattedMessage } from 'react-intl';
 | 
				
			||||||
 | 
					import Button from 'mastodon/components/button';
 | 
				
			||||||
 | 
					import Option from './components/option';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const mapStateToProps = state => ({
 | 
				
			||||||
 | 
					  rules: state.get('rules'),
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default @connect(mapStateToProps)
 | 
				
			||||||
 | 
					class Rules extends React.PureComponent {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static propTypes = {
 | 
				
			||||||
 | 
					    onNextStep: PropTypes.func.isRequired,
 | 
				
			||||||
 | 
					    rules: ImmutablePropTypes.list,
 | 
				
			||||||
 | 
					    selectedRuleIds: ImmutablePropTypes.set.isRequired,
 | 
				
			||||||
 | 
					    onToggle: PropTypes.func.isRequired,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleNextClick = () => {
 | 
				
			||||||
 | 
					    const { onNextStep } = this.props;
 | 
				
			||||||
 | 
					    onNextStep('statuses');
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleRulesToggle = (value, checked) => {
 | 
				
			||||||
 | 
					    const { onToggle } = this.props;
 | 
				
			||||||
 | 
					    onToggle(value, checked);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render () {
 | 
				
			||||||
 | 
					    const { rules, selectedRuleIds } = this.props;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <React.Fragment>
 | 
				
			||||||
 | 
					        <h3 className='report-dialog-modal__title'><FormattedMessage id='report.rules.title' defaultMessage='Which rules are being violated?' /></h3>
 | 
				
			||||||
 | 
					        <p className='report-dialog-modal__lead'><FormattedMessage id='report.rules.subtitle' defaultMessage='Select all that apply' /></p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <div>
 | 
				
			||||||
 | 
					          {rules.map(item => (
 | 
				
			||||||
 | 
					            <Option
 | 
				
			||||||
 | 
					              key={item.get('id')}
 | 
				
			||||||
 | 
					              name='rule_ids'
 | 
				
			||||||
 | 
					              value={item.get('id')}
 | 
				
			||||||
 | 
					              checked={selectedRuleIds.includes(item.get('id'))}
 | 
				
			||||||
 | 
					              onToggle={this.handleRulesToggle}
 | 
				
			||||||
 | 
					              label={item.get('text')}
 | 
				
			||||||
 | 
					              multiple
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					          ))}
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <div className='flex-spacer' />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <div className='report-dialog-modal__actions'>
 | 
				
			||||||
 | 
					          <Button onClick={this.handleNextClick} disabled={selectedRuleIds.size < 1}><FormattedMessage id='report.next' defaultMessage='Next' /></Button>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </React.Fragment>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										58
									
								
								app/javascript/mastodon/features/report/statuses.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								app/javascript/mastodon/features/report/statuses.js
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,58 @@
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
 | 
					import ImmutablePropTypes from 'react-immutable-proptypes';
 | 
				
			||||||
 | 
					import { connect } from 'react-redux';
 | 
				
			||||||
 | 
					import StatusCheckBox from 'mastodon/features/report/containers/status_check_box_container';
 | 
				
			||||||
 | 
					import { OrderedSet } from 'immutable';
 | 
				
			||||||
 | 
					import { FormattedMessage } from 'react-intl';
 | 
				
			||||||
 | 
					import Button from 'mastodon/components/button';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const mapStateToProps = (state, { accountId }) => ({
 | 
				
			||||||
 | 
					  availableStatusIds: OrderedSet(state.getIn(['timelines', `account:${accountId}:with_replies`, 'items'])),
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default @connect(mapStateToProps)
 | 
				
			||||||
 | 
					class Statuses extends React.PureComponent {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static propTypes = {
 | 
				
			||||||
 | 
					    onNextStep: PropTypes.func.isRequired,
 | 
				
			||||||
 | 
					    accountId: PropTypes.string.isRequired,
 | 
				
			||||||
 | 
					    availableStatusIds: ImmutablePropTypes.set.isRequired,
 | 
				
			||||||
 | 
					    selectedStatusIds: ImmutablePropTypes.set.isRequired,
 | 
				
			||||||
 | 
					    onToggle: PropTypes.func.isRequired,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleNextClick = () => {
 | 
				
			||||||
 | 
					    const { onNextStep } = this.props;
 | 
				
			||||||
 | 
					    onNextStep('comment');
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render () {
 | 
				
			||||||
 | 
					    const { availableStatusIds, selectedStatusIds, onToggle } = this.props;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <React.Fragment>
 | 
				
			||||||
 | 
					        <h3 className='report-dialog-modal__title'><FormattedMessage id='report.statuses.title' defaultMessage='Are there any posts that back up this report?' /></h3>
 | 
				
			||||||
 | 
					        <p className='report-dialog-modal__lead'><FormattedMessage id='report.statuses.subtitle' defaultMessage='Select all that apply' /></p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <div className='report-dialog-modal__statuses'>
 | 
				
			||||||
 | 
					          {availableStatusIds.union(selectedStatusIds).map(statusId => (
 | 
				
			||||||
 | 
					            <StatusCheckBox
 | 
				
			||||||
 | 
					              id={statusId}
 | 
				
			||||||
 | 
					              key={statusId}
 | 
				
			||||||
 | 
					              checked={selectedStatusIds.includes(statusId)}
 | 
				
			||||||
 | 
					              onToggle={onToggle}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					          ))}
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <div className='flex-spacer' />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <div className='report-dialog-modal__actions'>
 | 
				
			||||||
 | 
					          <Button onClick={this.handleNextClick}><FormattedMessage id='report.next' defaultMessage='Next' /></Button>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </React.Fragment>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										84
									
								
								app/javascript/mastodon/features/report/thanks.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								app/javascript/mastodon/features/report/thanks.js
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,84 @@
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
 | 
					import ImmutablePropTypes from 'react-immutable-proptypes';
 | 
				
			||||||
 | 
					import { FormattedMessage } from 'react-intl';
 | 
				
			||||||
 | 
					import Button from 'mastodon/components/button';
 | 
				
			||||||
 | 
					import { connect } from 'react-redux';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  unfollowAccount,
 | 
				
			||||||
 | 
					  muteAccount,
 | 
				
			||||||
 | 
					  blockAccount,
 | 
				
			||||||
 | 
					} from 'mastodon/actions/accounts';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const mapStateToProps = () => ({});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default @connect(mapStateToProps)
 | 
				
			||||||
 | 
					class Thanks extends React.PureComponent {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static propTypes = {
 | 
				
			||||||
 | 
					    submitted: PropTypes.bool,
 | 
				
			||||||
 | 
					    onClose: PropTypes.func.isRequired,
 | 
				
			||||||
 | 
					    account: ImmutablePropTypes.map.isRequired,
 | 
				
			||||||
 | 
					    dispatch: PropTypes.func.isRequired,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleCloseClick = () => {
 | 
				
			||||||
 | 
					    const { onClose } = this.props;
 | 
				
			||||||
 | 
					    onClose();
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleUnfollowClick = () => {
 | 
				
			||||||
 | 
					    const { dispatch, account, onClose } = this.props;
 | 
				
			||||||
 | 
					    dispatch(unfollowAccount(account.get('id')));
 | 
				
			||||||
 | 
					    onClose();
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleMuteClick = () => {
 | 
				
			||||||
 | 
					    const { dispatch, account, onClose } = this.props;
 | 
				
			||||||
 | 
					    dispatch(muteAccount(account.get('id')));
 | 
				
			||||||
 | 
					    onClose();
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleBlockClick = () => {
 | 
				
			||||||
 | 
					    const { dispatch, account, onClose } = this.props;
 | 
				
			||||||
 | 
					    dispatch(blockAccount(account.get('id')));
 | 
				
			||||||
 | 
					    onClose();
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render () {
 | 
				
			||||||
 | 
					    const { account, submitted } = this.props;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <React.Fragment>
 | 
				
			||||||
 | 
					        <h3 className='report-dialog-modal__title'>{submitted ? <FormattedMessage id='report.thanks.title_actionable' defaultMessage="Thanks for reporting, we'll look into this." /> : <FormattedMessage id='report.thanks.title' defaultMessage="Don't want to see this?" />}</h3>
 | 
				
			||||||
 | 
					        <p className='report-dialog-modal__lead'>{submitted ? <FormattedMessage id='report.thanks.take_action_actionable' defaultMessage='While we review this, you can take action against @{name}:' values={{ name: account.get('username') }} /> : <FormattedMessage id='report.thanks.take_action' defaultMessage='Here are your options for controlling what you see on Mastodon:' />}</p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        {account.getIn(['relationship', 'following']) && (
 | 
				
			||||||
 | 
					          <React.Fragment>
 | 
				
			||||||
 | 
					            <h4 className='report-dialog-modal__subtitle'><FormattedMessage id='report.unfollow' defaultMessage='Unfollow @{name}' values={{ name: account.get('username') }} /></h4>
 | 
				
			||||||
 | 
					            <p className='report-dialog-modal__lead'><FormattedMessage id='report.unfollow_explanation' defaultMessage='You are following this account. To not see their posts in your home feed anymore, unfollow them.' /></p>
 | 
				
			||||||
 | 
					            <Button secondary onClick={this.handleUnfollowClick}><FormattedMessage id='account.unfollow' defaultMessage='Unfollow' /></Button>
 | 
				
			||||||
 | 
					            <hr />
 | 
				
			||||||
 | 
					          </React.Fragment>
 | 
				
			||||||
 | 
					        )}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <h4 className='report-dialog-modal__subtitle'><FormattedMessage id='account.mute' defaultMessage='Mute @{name}' values={{ name: account.get('username') }} /></h4>
 | 
				
			||||||
 | 
					        <p className='report-dialog-modal__lead'><FormattedMessage id='report.mute_explanation' defaultMessage='You will not see their posts. They can still follow you and see your posts and will not know that they are muted.' /></p>
 | 
				
			||||||
 | 
					        <Button secondary onClick={this.handleMuteClick}>{!account.getIn(['relationship', 'muting']) ? <FormattedMessage id='report.mute' defaultMessage='Mute' /> : <FormattedMessage id='account.muted' defaultMessage='Muted' />}</Button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <hr />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <h4 className='report-dialog-modal__subtitle'><FormattedMessage id='account.block' defaultMessage='Block @{name}' values={{ name: account.get('username') }} /></h4>
 | 
				
			||||||
 | 
					        <p className='report-dialog-modal__lead'><FormattedMessage id='report.block_explanation' defaultMessage='You will not see their posts. They will not be able to see your posts or follow you. They will be able to tell that they are blocked.' /></p>
 | 
				
			||||||
 | 
					        <Button secondary onClick={this.handleBlockClick}>{!account.getIn(['relationship', 'blocking']) ? <FormattedMessage id='report.block' defaultMessage='Block' /> : <FormattedMessage id='account.blocked' defaultMessage='Blocked' />}</Button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <div className='flex-spacer' />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <div className='report-dialog-modal__actions'>
 | 
				
			||||||
 | 
					          <Button onClick={this.handleCloseClick}><FormattedMessage id='report.close' defaultMessage='Done' /></Button>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </React.Fragment>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,38 +1,31 @@
 | 
				
			||||||
import React from 'react';
 | 
					import React from 'react';
 | 
				
			||||||
import { connect } from 'react-redux';
 | 
					import { connect } from 'react-redux';
 | 
				
			||||||
import { changeReportComment, changeReportForward, submitReport } from '../../../actions/reports';
 | 
					import { submitReport } from 'mastodon/actions/reports';
 | 
				
			||||||
import { expandAccountTimeline } from '../../../actions/timelines';
 | 
					import { expandAccountTimeline } from 'mastodon/actions/timelines';
 | 
				
			||||||
 | 
					import { fetchRules } from 'mastodon/actions/rules';
 | 
				
			||||||
import PropTypes from 'prop-types';
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
 | 
					import ImmutablePropTypes from 'react-immutable-proptypes';
 | 
				
			||||||
import { makeGetAccount } from '../../../selectors';
 | 
					import { makeGetAccount } from 'mastodon/selectors';
 | 
				
			||||||
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
 | 
					import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
 | 
				
			||||||
import StatusCheckBox from '../../report/containers/status_check_box_container';
 | 
					 | 
				
			||||||
import { OrderedSet } from 'immutable';
 | 
					import { OrderedSet } from 'immutable';
 | 
				
			||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
 | 
					import ImmutablePureComponent from 'react-immutable-pure-component';
 | 
				
			||||||
import Button from '../../../components/button';
 | 
					import IconButton from 'mastodon/components/icon_button';
 | 
				
			||||||
import Toggle from 'react-toggle';
 | 
					import Category from 'mastodon/features/report/category';
 | 
				
			||||||
import IconButton from '../../../components/icon_button';
 | 
					import Statuses from 'mastodon/features/report/statuses';
 | 
				
			||||||
 | 
					import Rules from 'mastodon/features/report/rules';
 | 
				
			||||||
 | 
					import Comment from 'mastodon/features/report/comment';
 | 
				
			||||||
 | 
					import Thanks from 'mastodon/features/report/thanks';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const messages = defineMessages({
 | 
					const messages = defineMessages({
 | 
				
			||||||
  close: { id: 'lightbox.close', defaultMessage: 'Close' },
 | 
					  close: { id: 'lightbox.close', defaultMessage: 'Close' },
 | 
				
			||||||
  placeholder: { id: 'report.placeholder', defaultMessage: 'Additional comments' },
 | 
					 | 
				
			||||||
  submit: { id: 'report.submit', defaultMessage: 'Submit' },
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const makeMapStateToProps = () => {
 | 
					const makeMapStateToProps = () => {
 | 
				
			||||||
  const getAccount = makeGetAccount();
 | 
					  const getAccount = makeGetAccount();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const mapStateToProps = state => {
 | 
					  const mapStateToProps = (state, { accountId }) => ({
 | 
				
			||||||
    const accountId = state.getIn(['reports', 'new', 'account_id']);
 | 
					    account: getAccount(state, accountId),
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
    return {
 | 
					 | 
				
			||||||
      isSubmitting: state.getIn(['reports', 'new', 'isSubmitting']),
 | 
					 | 
				
			||||||
      account: getAccount(state, accountId),
 | 
					 | 
				
			||||||
      comment: state.getIn(['reports', 'new', 'comment']),
 | 
					 | 
				
			||||||
      forward: state.getIn(['reports', 'new', 'forward']),
 | 
					 | 
				
			||||||
      statusIds: OrderedSet(state.getIn(['timelines', `account:${accountId}:with_replies`, 'items'])).union(state.getIn(['reports', 'new', 'status_ids'])),
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return mapStateToProps;
 | 
					  return mapStateToProps;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -42,92 +35,182 @@ export default @connect(makeMapStateToProps)
 | 
				
			||||||
class ReportModal extends ImmutablePureComponent {
 | 
					class ReportModal extends ImmutablePureComponent {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static propTypes = {
 | 
					  static propTypes = {
 | 
				
			||||||
    isSubmitting: PropTypes.bool,
 | 
					    accountId: PropTypes.string.isRequired,
 | 
				
			||||||
    account: ImmutablePropTypes.map,
 | 
					    statusId: PropTypes.string,
 | 
				
			||||||
    statusIds: ImmutablePropTypes.orderedSet.isRequired,
 | 
					 | 
				
			||||||
    comment: PropTypes.string.isRequired,
 | 
					 | 
				
			||||||
    forward: PropTypes.bool,
 | 
					 | 
				
			||||||
    dispatch: PropTypes.func.isRequired,
 | 
					    dispatch: PropTypes.func.isRequired,
 | 
				
			||||||
    intl: PropTypes.object.isRequired,
 | 
					    intl: PropTypes.object.isRequired,
 | 
				
			||||||
 | 
					    account: ImmutablePropTypes.map.isRequired,
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  handleCommentChange = e => {
 | 
					  state = {
 | 
				
			||||||
    this.props.dispatch(changeReportComment(e.target.value));
 | 
					    step: 'category',
 | 
				
			||||||
  }
 | 
					    selectedStatusIds: OrderedSet(this.props.statusId ? [this.props.statusId] : []),
 | 
				
			||||||
 | 
					    comment: '',
 | 
				
			||||||
  handleForwardChange = e => {
 | 
					    category: null,
 | 
				
			||||||
    this.props.dispatch(changeReportForward(e.target.checked));
 | 
					    selectedRuleIds: OrderedSet(),
 | 
				
			||||||
  }
 | 
					    forward: true,
 | 
				
			||||||
 | 
					    isSubmitting: false,
 | 
				
			||||||
 | 
					    isSubmitted: false,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  handleSubmit = () => {
 | 
					  handleSubmit = () => {
 | 
				
			||||||
    this.props.dispatch(submitReport());
 | 
					    const { dispatch, accountId } = this.props;
 | 
				
			||||||
  }
 | 
					    const { selectedStatusIds, comment, category, selectedRuleIds, forward } = this.state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  handleKeyDown = e => {
 | 
					    this.setState({ isSubmitting: true });
 | 
				
			||||||
    if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
 | 
					
 | 
				
			||||||
      this.handleSubmit();
 | 
					    dispatch(submitReport({
 | 
				
			||||||
 | 
					      account_id: accountId,
 | 
				
			||||||
 | 
					      status_ids: selectedStatusIds.toArray(),
 | 
				
			||||||
 | 
					      comment,
 | 
				
			||||||
 | 
					      forward,
 | 
				
			||||||
 | 
					      category,
 | 
				
			||||||
 | 
					      rule_ids: selectedRuleIds.toArray(),
 | 
				
			||||||
 | 
					    }, this.handleSuccess, this.handleFail));
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleSuccess = () => {
 | 
				
			||||||
 | 
					    this.setState({ isSubmitting: false, isSubmitted: true, step: 'thanks' });
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleFail = () => {
 | 
				
			||||||
 | 
					    this.setState({ isSubmitting: false });
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleStatusToggle = (statusId, checked) => {
 | 
				
			||||||
 | 
					    const { selectedStatusIds } = this.state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (checked) {
 | 
				
			||||||
 | 
					      this.setState({ selectedStatusIds: selectedStatusIds.add(statusId) });
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      this.setState({ selectedStatusIds: selectedStatusIds.remove(statusId) });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleRuleToggle = (ruleId, checked) => {
 | 
				
			||||||
 | 
					    const { selectedRuleIds } = this.state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (checked) {
 | 
				
			||||||
 | 
					      this.setState({ selectedRuleIds: selectedRuleIds.add(ruleId) });
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      this.setState({ selectedRuleIds: selectedRuleIds.remove(ruleId) });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleChangeCategory = category => {
 | 
				
			||||||
 | 
					    this.setState({ category });
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleChangeComment = comment => {
 | 
				
			||||||
 | 
					    this.setState({ comment });
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleChangeForward = forward => {
 | 
				
			||||||
 | 
					    this.setState({ forward });
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleNextStep = step => {
 | 
				
			||||||
 | 
					    this.setState({ step });
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  componentDidMount () {
 | 
					  componentDidMount () {
 | 
				
			||||||
    this.props.dispatch(expandAccountTimeline(this.props.account.get('id'), { withReplies: true }));
 | 
					    const { dispatch, accountId } = this.props;
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  componentWillReceiveProps (nextProps) {
 | 
					    dispatch(expandAccountTimeline(accountId, { withReplies: true }));
 | 
				
			||||||
    if (this.props.account !== nextProps.account && nextProps.account) {
 | 
					    dispatch(fetchRules());
 | 
				
			||||||
      this.props.dispatch(expandAccountTimeline(nextProps.account.get('id'), { withReplies: true }));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  render () {
 | 
					  render () {
 | 
				
			||||||
    const { account, comment, intl, statusIds, isSubmitting, forward, onClose } = this.props;
 | 
					    const {
 | 
				
			||||||
 | 
					      accountId,
 | 
				
			||||||
 | 
					      account,
 | 
				
			||||||
 | 
					      intl,
 | 
				
			||||||
 | 
					      onClose,
 | 
				
			||||||
 | 
					    } = this.props;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!account) {
 | 
					    if (!account) {
 | 
				
			||||||
      return null;
 | 
					      return null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const domain = account.get('acct').split('@')[1];
 | 
					    const {
 | 
				
			||||||
 | 
					      step,
 | 
				
			||||||
 | 
					      selectedStatusIds,
 | 
				
			||||||
 | 
					      selectedRuleIds,
 | 
				
			||||||
 | 
					      comment,
 | 
				
			||||||
 | 
					      forward,
 | 
				
			||||||
 | 
					      category,
 | 
				
			||||||
 | 
					      isSubmitting,
 | 
				
			||||||
 | 
					      isSubmitted,
 | 
				
			||||||
 | 
					    } = this.state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const domain   = account.get('acct').split('@')[1];
 | 
				
			||||||
 | 
					    const isRemote = !!domain;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let stepComponent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch(step) {
 | 
				
			||||||
 | 
					    case 'category':
 | 
				
			||||||
 | 
					      stepComponent = (
 | 
				
			||||||
 | 
					        <Category
 | 
				
			||||||
 | 
					          onNextStep={this.handleNextStep}
 | 
				
			||||||
 | 
					          startedFrom={this.props.statusId ? 'status' : 'account'}
 | 
				
			||||||
 | 
					          category={category}
 | 
				
			||||||
 | 
					          onChangeCategory={this.handleChangeCategory}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    case 'rules':
 | 
				
			||||||
 | 
					      stepComponent = (
 | 
				
			||||||
 | 
					        <Rules
 | 
				
			||||||
 | 
					          onNextStep={this.handleNextStep}
 | 
				
			||||||
 | 
					          selectedRuleIds={selectedRuleIds}
 | 
				
			||||||
 | 
					          onToggle={this.handleRuleToggle}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    case 'statuses':
 | 
				
			||||||
 | 
					      stepComponent = (
 | 
				
			||||||
 | 
					        <Statuses
 | 
				
			||||||
 | 
					          onNextStep={this.handleNextStep}
 | 
				
			||||||
 | 
					          accountId={accountId}
 | 
				
			||||||
 | 
					          selectedStatusIds={selectedStatusIds}
 | 
				
			||||||
 | 
					          onToggle={this.handleStatusToggle}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    case 'comment':
 | 
				
			||||||
 | 
					      stepComponent = (
 | 
				
			||||||
 | 
					        <Comment
 | 
				
			||||||
 | 
					          onSubmit={this.handleSubmit}
 | 
				
			||||||
 | 
					          isSubmitting={isSubmitting}
 | 
				
			||||||
 | 
					          isRemote={isRemote}
 | 
				
			||||||
 | 
					          comment={comment}
 | 
				
			||||||
 | 
					          forward={forward}
 | 
				
			||||||
 | 
					          domain={domain}
 | 
				
			||||||
 | 
					          onChangeComment={this.handleChangeComment}
 | 
				
			||||||
 | 
					          onChangeForward={this.handleChangeForward}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    case 'thanks':
 | 
				
			||||||
 | 
					      stepComponent = (
 | 
				
			||||||
 | 
					        <Thanks
 | 
				
			||||||
 | 
					          submitted={isSubmitted}
 | 
				
			||||||
 | 
					          account={account}
 | 
				
			||||||
 | 
					          onClose={onClose}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <div className='modal-root__modal report-modal'>
 | 
					      <div className='modal-root__modal report-dialog-modal'>
 | 
				
			||||||
        <div className='report-modal__target'>
 | 
					        <div className='report-modal__target'>
 | 
				
			||||||
          <IconButton className='report-modal__close' title={intl.formatMessage(messages.close)} icon='times' onClick={onClose} size={20} />
 | 
					          <IconButton className='report-modal__close' title={intl.formatMessage(messages.close)} icon='times' onClick={onClose} size={20} />
 | 
				
			||||||
          <FormattedMessage id='report.target' defaultMessage='Report {target}' values={{ target: <strong>{account.get('acct')}</strong> }} />
 | 
					          <FormattedMessage id='report.target' defaultMessage='Report {target}' values={{ target: <strong>{account.get('acct')}</strong> }} />
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <div className='report-modal__container'>
 | 
					        <div className='report-dialog-modal__container'>
 | 
				
			||||||
          <div className='report-modal__comment'>
 | 
					          {stepComponent}
 | 
				
			||||||
            <p><FormattedMessage id='report.hint' defaultMessage='The report will be sent to your server moderators. You can provide an explanation of why you are reporting this account below:' /></p>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <textarea
 | 
					 | 
				
			||||||
              className='setting-text light'
 | 
					 | 
				
			||||||
              placeholder={intl.formatMessage(messages.placeholder)}
 | 
					 | 
				
			||||||
              value={comment}
 | 
					 | 
				
			||||||
              onChange={this.handleCommentChange}
 | 
					 | 
				
			||||||
              onKeyDown={this.handleKeyDown}
 | 
					 | 
				
			||||||
              disabled={isSubmitting}
 | 
					 | 
				
			||||||
              autoFocus
 | 
					 | 
				
			||||||
            />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            {domain && (
 | 
					 | 
				
			||||||
              <div>
 | 
					 | 
				
			||||||
                <p><FormattedMessage id='report.forward_hint' defaultMessage='The account is from another server. Send an anonymized copy of the report there as well?' /></p>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <div className='setting-toggle'>
 | 
					 | 
				
			||||||
                  <Toggle id='report-forward' checked={forward} disabled={isSubmitting} onChange={this.handleForwardChange} />
 | 
					 | 
				
			||||||
                  <label htmlFor='report-forward' className='setting-toggle__label'><FormattedMessage id='report.forward' defaultMessage='Forward to {target}' values={{ target: domain }} /></label>
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
              </div>
 | 
					 | 
				
			||||||
            )}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <Button disabled={isSubmitting} text={intl.formatMessage(messages.submit)} onClick={this.handleSubmit} />
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          <div className='report-modal__statuses'>
 | 
					 | 
				
			||||||
            <div>
 | 
					 | 
				
			||||||
              {statusIds.map(statusId => <StatusCheckBox id={statusId} key={statusId} disabled={isSubmitting} />)}
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,7 +17,8 @@ import status_lists from './status_lists';
 | 
				
			||||||
import mutes from './mutes';
 | 
					import mutes from './mutes';
 | 
				
			||||||
import blocks from './blocks';
 | 
					import blocks from './blocks';
 | 
				
			||||||
import boosts from './boosts';
 | 
					import boosts from './boosts';
 | 
				
			||||||
import reports from './reports';
 | 
					// import reports from './reports';
 | 
				
			||||||
 | 
					import rules from './rules';
 | 
				
			||||||
import contexts from './contexts';
 | 
					import contexts from './contexts';
 | 
				
			||||||
import compose from './compose';
 | 
					import compose from './compose';
 | 
				
			||||||
import search from './search';
 | 
					import search from './search';
 | 
				
			||||||
| 
						 | 
					@ -61,7 +62,8 @@ const reducers = {
 | 
				
			||||||
  mutes,
 | 
					  mutes,
 | 
				
			||||||
  blocks,
 | 
					  blocks,
 | 
				
			||||||
  boosts,
 | 
					  boosts,
 | 
				
			||||||
  reports,
 | 
					  // reports,
 | 
				
			||||||
 | 
					  rules,
 | 
				
			||||||
  contexts,
 | 
					  contexts,
 | 
				
			||||||
  compose,
 | 
					  compose,
 | 
				
			||||||
  search,
 | 
					  search,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										13
									
								
								app/javascript/mastodon/reducers/rules.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								app/javascript/mastodon/reducers/rules.js
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,13 @@
 | 
				
			||||||
 | 
					import { RULES_FETCH_SUCCESS } from 'mastodon/actions/rules';
 | 
				
			||||||
 | 
					import { List as ImmutableList, fromJS } from 'immutable';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const initialState = ImmutableList();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function rules(state = initialState, action) {
 | 
				
			||||||
 | 
					  switch (action.type) {
 | 
				
			||||||
 | 
					  case RULES_FETCH_SUCCESS:
 | 
				
			||||||
 | 
					    return fromJS(action.rules);
 | 
				
			||||||
 | 
					  default:
 | 
				
			||||||
 | 
					    return state;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -50,16 +50,14 @@
 | 
				
			||||||
  cursor: pointer;
 | 
					  cursor: pointer;
 | 
				
			||||||
  display: inline-block;
 | 
					  display: inline-block;
 | 
				
			||||||
  font-family: inherit;
 | 
					  font-family: inherit;
 | 
				
			||||||
  font-size: 14px;
 | 
					  font-size: 17px;
 | 
				
			||||||
  font-weight: 500;
 | 
					  font-weight: 500;
 | 
				
			||||||
  height: 36px;
 | 
					 | 
				
			||||||
  letter-spacing: 0;
 | 
					  letter-spacing: 0;
 | 
				
			||||||
  line-height: 36px;
 | 
					  line-height: 22px;
 | 
				
			||||||
  overflow: hidden;
 | 
					  overflow: hidden;
 | 
				
			||||||
  padding: 0 16px;
 | 
					  padding: 7px 18px;
 | 
				
			||||||
  position: relative;
 | 
					  position: relative;
 | 
				
			||||||
  text-align: center;
 | 
					  text-align: center;
 | 
				
			||||||
  text-transform: uppercase;
 | 
					 | 
				
			||||||
  text-decoration: none;
 | 
					  text-decoration: none;
 | 
				
			||||||
  text-overflow: ellipsis;
 | 
					  text-overflow: ellipsis;
 | 
				
			||||||
  transition: all 100ms ease-in;
 | 
					  transition: all 100ms ease-in;
 | 
				
			||||||
| 
						 | 
					@ -100,17 +98,6 @@
 | 
				
			||||||
    outline: 0 !important;
 | 
					    outline: 0 !important;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  &.button-primary,
 | 
					 | 
				
			||||||
  &.button-alternative,
 | 
					 | 
				
			||||||
  &.button-secondary,
 | 
					 | 
				
			||||||
  &.button-alternative-2 {
 | 
					 | 
				
			||||||
    font-size: 16px;
 | 
					 | 
				
			||||||
    line-height: 36px;
 | 
					 | 
				
			||||||
    height: auto;
 | 
					 | 
				
			||||||
    text-transform: none;
 | 
					 | 
				
			||||||
    padding: 4px 16px;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  &.button-alternative {
 | 
					  &.button-alternative {
 | 
				
			||||||
    color: $inverted-text-color;
 | 
					    color: $inverted-text-color;
 | 
				
			||||||
    background: $ui-primary-color;
 | 
					    background: $ui-primary-color;
 | 
				
			||||||
| 
						 | 
					@ -135,7 +122,7 @@
 | 
				
			||||||
  &.button-secondary {
 | 
					  &.button-secondary {
 | 
				
			||||||
    color: $darker-text-color;
 | 
					    color: $darker-text-color;
 | 
				
			||||||
    background: transparent;
 | 
					    background: transparent;
 | 
				
			||||||
    padding: 3px 15px;
 | 
					    padding: 6px 17px;
 | 
				
			||||||
    border: 1px solid $ui-primary-color;
 | 
					    border: 1px solid $ui-primary-color;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    &:active,
 | 
					    &:active,
 | 
				
			||||||
| 
						 | 
					@ -1114,42 +1101,39 @@
 | 
				
			||||||
  font-size: 15px;
 | 
					  font-size: 15px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.status-check-box {
 | 
					.status-check-box__status {
 | 
				
			||||||
  border-bottom: 1px solid $ui-secondary-color;
 | 
					  display: block;
 | 
				
			||||||
  display: flex;
 | 
					  box-sizing: border-box;
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					  padding: 0 10px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  .status-check-box__status {
 | 
					  .detailed-status__display-name {
 | 
				
			||||||
    margin: 10px 0 10px 10px;
 | 
					    color: lighten($inverted-text-color, 16%);
 | 
				
			||||||
    flex: 1;
 | 
					 | 
				
			||||||
    overflow: hidden;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .media-gallery {
 | 
					    span {
 | 
				
			||||||
      max-width: 250px;
 | 
					      display: inline;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .status__content {
 | 
					    &:hover strong {
 | 
				
			||||||
      padding: 0;
 | 
					      text-decoration: none;
 | 
				
			||||||
      white-space: normal;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .video-player,
 | 
					 | 
				
			||||||
    .audio-player {
 | 
					 | 
				
			||||||
      margin-top: 8px;
 | 
					 | 
				
			||||||
      max-width: 250px;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .media-gallery__item-thumbnail {
 | 
					 | 
				
			||||||
      cursor: default;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
.status-check-box-toggle {
 | 
					  .media-gallery,
 | 
				
			||||||
  align-items: center;
 | 
					  .audio-player,
 | 
				
			||||||
  display: flex;
 | 
					  .video-player {
 | 
				
			||||||
  flex: 0 0 auto;
 | 
					    margin-top: 8px;
 | 
				
			||||||
  justify-content: center;
 | 
					    max-width: 250px;
 | 
				
			||||||
  padding: 10px;
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .status__content {
 | 
				
			||||||
 | 
					    padding: 0;
 | 
				
			||||||
 | 
					    white-space: normal;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .media-gallery__item-thumbnail {
 | 
				
			||||||
 | 
					    cursor: default;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.status__prepend {
 | 
					.status__prepend {
 | 
				
			||||||
| 
						 | 
					@ -5103,6 +5087,192 @@ a.status-card.compact:hover {
 | 
				
			||||||
  max-width: 700px;
 | 
					  max-width: 700px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.report-dialog-modal {
 | 
				
			||||||
 | 
					  max-width: 90vw;
 | 
				
			||||||
 | 
					  width: 480px;
 | 
				
			||||||
 | 
					  height: 80vh;
 | 
				
			||||||
 | 
					  background: lighten($ui-secondary-color, 8%);
 | 
				
			||||||
 | 
					  color: $inverted-text-color;
 | 
				
			||||||
 | 
					  border-radius: 8px;
 | 
				
			||||||
 | 
					  overflow: hidden;
 | 
				
			||||||
 | 
					  position: relative;
 | 
				
			||||||
 | 
					  flex-direction: column;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  &__container {
 | 
				
			||||||
 | 
					    box-sizing: border-box;
 | 
				
			||||||
 | 
					    border-top: 1px solid $ui-secondary-color;
 | 
				
			||||||
 | 
					    padding: 20px;
 | 
				
			||||||
 | 
					    flex-grow: 1;
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					    flex-direction: column;
 | 
				
			||||||
 | 
					    min-height: 0;
 | 
				
			||||||
 | 
					    overflow: auto;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  &__title {
 | 
				
			||||||
 | 
					    font-size: 28px;
 | 
				
			||||||
 | 
					    line-height: 33px;
 | 
				
			||||||
 | 
					    font-weight: 700;
 | 
				
			||||||
 | 
					    margin-bottom: 15px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @media screen and (max-height: 800px) {
 | 
				
			||||||
 | 
					      font-size: 22px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  &__subtitle {
 | 
				
			||||||
 | 
					    font-size: 17px;
 | 
				
			||||||
 | 
					    font-weight: 600;
 | 
				
			||||||
 | 
					    line-height: 22px;
 | 
				
			||||||
 | 
					    margin-bottom: 4px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  &__lead {
 | 
				
			||||||
 | 
					    font-size: 17px;
 | 
				
			||||||
 | 
					    line-height: 22px;
 | 
				
			||||||
 | 
					    color: lighten($inverted-text-color, 16%);
 | 
				
			||||||
 | 
					    margin-bottom: 30px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  &__actions {
 | 
				
			||||||
 | 
					    margin-top: 30px;
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .button {
 | 
				
			||||||
 | 
					      flex: 1 1 auto;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  &__statuses {
 | 
				
			||||||
 | 
					    flex-grow: 1;
 | 
				
			||||||
 | 
					    min-height: 0;
 | 
				
			||||||
 | 
					    overflow: auto;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .status__content a {
 | 
				
			||||||
 | 
					    color: $highlight-text-color;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .status__content,
 | 
				
			||||||
 | 
					  .status__content p {
 | 
				
			||||||
 | 
					    color: $inverted-text-color;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .dialog-option .poll__input {
 | 
				
			||||||
 | 
					    border-color: $inverted-text-color;
 | 
				
			||||||
 | 
					    color: $ui-secondary-color;
 | 
				
			||||||
 | 
					    display: inline-flex;
 | 
				
			||||||
 | 
					    align-items: center;
 | 
				
			||||||
 | 
					    justify-content: center;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    svg {
 | 
				
			||||||
 | 
					      width: 8px;
 | 
				
			||||||
 | 
					      height: auto;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &:active,
 | 
				
			||||||
 | 
					    &:focus,
 | 
				
			||||||
 | 
					    &:hover {
 | 
				
			||||||
 | 
					      border-color: lighten($inverted-text-color, 15%);
 | 
				
			||||||
 | 
					      border-width: 4px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &.active {
 | 
				
			||||||
 | 
					      border-color: $inverted-text-color;
 | 
				
			||||||
 | 
					      background: $inverted-text-color;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .poll__option.dialog-option {
 | 
				
			||||||
 | 
					    padding: 15px 0;
 | 
				
			||||||
 | 
					    flex: 0 0 auto;
 | 
				
			||||||
 | 
					    border-bottom: 1px solid $ui-secondary-color;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &:last-child {
 | 
				
			||||||
 | 
					      border-bottom: 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    & > .poll__option__text {
 | 
				
			||||||
 | 
					      font-size: 13px;
 | 
				
			||||||
 | 
					      color: lighten($inverted-text-color, 16%);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      strong {
 | 
				
			||||||
 | 
					        font-size: 17px;
 | 
				
			||||||
 | 
					        font-weight: 500;
 | 
				
			||||||
 | 
					        line-height: 22px;
 | 
				
			||||||
 | 
					        color: $inverted-text-color;
 | 
				
			||||||
 | 
					        display: block;
 | 
				
			||||||
 | 
					        margin-bottom: 4px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        &:last-child {
 | 
				
			||||||
 | 
					          margin-bottom: 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .flex-spacer {
 | 
				
			||||||
 | 
					    background: transparent;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  &__textarea {
 | 
				
			||||||
 | 
					    display: block;
 | 
				
			||||||
 | 
					    box-sizing: border-box;
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					    margin: 0;
 | 
				
			||||||
 | 
					    color: $inverted-text-color;
 | 
				
			||||||
 | 
					    background: $simple-background-color;
 | 
				
			||||||
 | 
					    padding: 10px;
 | 
				
			||||||
 | 
					    font-family: inherit;
 | 
				
			||||||
 | 
					    font-size: 17px;
 | 
				
			||||||
 | 
					    line-height: 22px;
 | 
				
			||||||
 | 
					    resize: vertical;
 | 
				
			||||||
 | 
					    border: 0;
 | 
				
			||||||
 | 
					    outline: 0;
 | 
				
			||||||
 | 
					    border-radius: 4px;
 | 
				
			||||||
 | 
					    margin: 20px 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &::placeholder {
 | 
				
			||||||
 | 
					      color: $dark-text-color;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &:focus {
 | 
				
			||||||
 | 
					      outline: 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  &__toggle {
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					    align-items: center;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    & > span {
 | 
				
			||||||
 | 
					      font-size: 17px;
 | 
				
			||||||
 | 
					      font-weight: 500;
 | 
				
			||||||
 | 
					      margin-left: 10px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .button.button-secondary {
 | 
				
			||||||
 | 
					    border-color: $inverted-text-color;
 | 
				
			||||||
 | 
					    color: $inverted-text-color;
 | 
				
			||||||
 | 
					    flex: 0 0 auto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &:hover,
 | 
				
			||||||
 | 
					    &:focus,
 | 
				
			||||||
 | 
					    &:active {
 | 
				
			||||||
 | 
					      border-color: lighten($inverted-text-color, 15%);
 | 
				
			||||||
 | 
					      color: lighten($inverted-text-color, 15%);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  hr {
 | 
				
			||||||
 | 
					    border: 0;
 | 
				
			||||||
 | 
					    background: transparent;
 | 
				
			||||||
 | 
					    margin: 15px 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.report-modal__container {
 | 
					.report-modal__container {
 | 
				
			||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
  border-top: 1px solid $ui-secondary-color;
 | 
					  border-top: 1px solid $ui-secondary-color;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue