forked from berserker/microblog
Merge branch 'main' of https://github.com/glitch-soc/mastodon into main
commit
655573f9c5
111 changed files with 2567 additions and 791 deletions
@ -1,89 +1,38 @@ |
|||||||
import api from 'flavours/glitch/util/api'; |
import api from 'flavours/glitch/util/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) { |
})); |
||||||
return dispatch => { |
|
||||||
dispatch({ |
export const submitReport = (params, onSuccess, onFail) => (dispatch, getState) => { |
||||||
type: REPORT_INIT, |
dispatch(submitReportRequest()); |
||||||
account, |
|
||||||
status, |
api(getState).post('/api/v1/reports', params).then(response => { |
||||||
}); |
dispatch(submitReportSuccess(response.data)); |
||||||
|
if (onSuccess) onSuccess(); |
||||||
dispatch(openModal('REPORT')); |
}).catch(error => { |
||||||
}; |
dispatch(submitReportFail(error)); |
||||||
}; |
if (onFail) onFail(); |
||||||
|
}); |
||||||
export function cancelReport() { |
}; |
||||||
return { |
|
||||||
type: REPORT_CANCEL, |
export const submitReportRequest = () => ({ |
||||||
}; |
type: REPORT_SUBMIT_REQUEST, |
||||||
}; |
}); |
||||||
|
|
||||||
export function toggleStatusReport(statusId, checked) { |
export const submitReportSuccess = report => ({ |
||||||
return { |
type: REPORT_SUBMIT_SUCCESS, |
||||||
type: REPORT_STATUS_TOGGLE, |
report, |
||||||
statusId, |
}); |
||||||
checked, |
|
||||||
}; |
export const submitReportFail = error => ({ |
||||||
}; |
type: REPORT_SUBMIT_FAIL, |
||||||
|
error, |
||||||
export function submitReport() { |
}); |
||||||
return (dispatch, getState) => { |
|
||||||
dispatch(submitReportRequest()); |
|
||||||
|
|
||||||
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, |
|
||||||
}; |
|
||||||
}; |
|
||||||
|
@ -0,0 +1,27 @@ |
|||||||
|
import api from 'flavours/glitch/util/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, |
||||||
|
}); |
@ -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; |
@ -0,0 +1,101 @@ |
|||||||
|
// Package imports.
|
||||||
|
import React from 'react'; |
||||||
|
import ImmutablePropTypes from 'react-immutable-proptypes'; |
||||||
|
import PropTypes from 'prop-types'; |
||||||
|
import { FormattedMessage } from 'react-intl'; |
||||||
|
import ImmutablePureComponent from 'react-immutable-pure-component'; |
||||||
|
import { HotKeys } from 'react-hotkeys'; |
||||||
|
import classNames from 'classnames'; |
||||||
|
|
||||||
|
// Our imports.
|
||||||
|
import Permalink from 'flavours/glitch/components/permalink'; |
||||||
|
import AccountContainer from 'flavours/glitch/containers/account_container'; |
||||||
|
import NotificationOverlayContainer from '../containers/overlay_container'; |
||||||
|
import Icon from 'flavours/glitch/components/icon'; |
||||||
|
|
||||||
|
export default class NotificationFollow extends ImmutablePureComponent { |
||||||
|
|
||||||
|
static propTypes = { |
||||||
|
hidden: PropTypes.bool, |
||||||
|
id: PropTypes.string.isRequired, |
||||||
|
account: ImmutablePropTypes.map.isRequired, |
||||||
|
notification: ImmutablePropTypes.map.isRequired, |
||||||
|
unread: PropTypes.bool, |
||||||
|
}; |
||||||
|
|
||||||
|
handleMoveUp = () => { |
||||||
|
const { notification, onMoveUp } = this.props; |
||||||
|
onMoveUp(notification.get('id')); |
||||||
|
} |
||||||
|
|
||||||
|
handleMoveDown = () => { |
||||||
|
const { notification, onMoveDown } = this.props; |
||||||
|
onMoveDown(notification.get('id')); |
||||||
|
} |
||||||
|
|
||||||
|
handleOpen = () => { |
||||||
|
this.handleOpenProfile(); |
||||||
|
} |
||||||
|
|
||||||
|
handleOpenProfile = () => { |
||||||
|
const { notification } = this.props; |
||||||
|
this.context.router.history.push(`/@${notification.getIn(['account', 'acct'])}`); |
||||||
|
} |
||||||
|
|
||||||
|
handleMention = e => { |
||||||
|
e.preventDefault(); |
||||||
|
|
||||||
|
const { notification, onMention } = this.props; |
||||||
|
onMention(notification.get('account'), this.context.router.history); |
||||||
|
} |
||||||
|
|
||||||
|
getHandlers () { |
||||||
|
return { |
||||||
|
moveUp: this.handleMoveUp, |
||||||
|
moveDown: this.handleMoveDown, |
||||||
|
open: this.handleOpen, |
||||||
|
openProfile: this.handleOpenProfile, |
||||||
|
mention: this.handleMention, |
||||||
|
reply: this.handleMention, |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
render () { |
||||||
|
const { account, notification, hidden, unread } = this.props; |
||||||
|
|
||||||
|
// Links to the display name.
|
||||||
|
const displayName = account.get('display_name_html') || account.get('username'); |
||||||
|
const link = ( |
||||||
|
<bdi><Permalink |
||||||
|
className='notification__display-name' |
||||||
|
href={account.get('url')} |
||||||
|
title={account.get('acct')} |
||||||
|
to={`/@${account.get('acct')}`} |
||||||
|
dangerouslySetInnerHTML={{ __html: displayName }} |
||||||
|
/></bdi> |
||||||
|
); |
||||||
|
|
||||||
|
// Renders.
|
||||||
|
return ( |
||||||
|
<HotKeys handlers={this.getHandlers()}> |
||||||
|
<div className={classNames('notification notification-admin-sign-up focusable', { unread })} tabIndex='0'> |
||||||
|
<div className='notification__message'> |
||||||
|
<div className='notification__favourite-icon-wrapper'> |
||||||
|
<Icon fixedWidth id='user-plus' /> |
||||||
|
</div> |
||||||
|
|
||||||
|
<FormattedMessage |
||||||
|
id='notification.admin.sign_up' |
||||||
|
defaultMessage='{name} signed up' |
||||||
|
values={{ name: link }} |
||||||
|
/> |
||||||
|
</div> |
||||||
|
|
||||||
|
<AccountContainer hidden={hidden} id={account.get('id')} withNote={false} /> |
||||||
|
<NotificationOverlayContainer notification={notification} /> |
||||||
|
</div> |
||||||
|
</HotKeys> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,93 @@ |
|||||||
|
import React from 'react'; |
||||||
|
import PropTypes from 'prop-types'; |
||||||
|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; |
||||||
|
import Button from 'flavours/glitch/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> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,83 @@ |
|||||||
|
import React from 'react'; |
||||||
|
import PropTypes from 'prop-types'; |
||||||
|
import { injectIntl, defineMessages, FormattedMessage } from 'react-intl'; |
||||||
|
import Button from 'flavours/glitch/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> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,60 @@ |
|||||||
|
import React from 'react'; |
||||||
|
import PropTypes from 'prop-types'; |
||||||
|
import classNames from 'classnames'; |
||||||
|
import Check from 'flavours/glitch/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,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 'flavours/glitch/actions/reports'; |
import { makeGetStatus } from 'flavours/glitch/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); |
|
||||||
|
@ -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 'flavours/glitch/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> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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 'flavours/glitch/features/report/containers/status_check_box_container'; |
||||||
|
import { OrderedSet } from 'immutable'; |
||||||
|
import { FormattedMessage } from 'react-intl'; |
||||||
|
import Button from 'flavours/glitch/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> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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 'flavours/glitch/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,77 +0,0 @@ |
|||||||
import { |
|
||||||
REPORT_INIT, |
|
||||||
REPORT_SUBMIT_REQUEST, |
|
||||||
REPORT_SUBMIT_SUCCESS, |
|
||||||
REPORT_SUBMIT_FAIL, |
|
||||||
REPORT_CANCEL, |
|
||||||
REPORT_STATUS_TOGGLE, |
|
||||||
REPORT_COMMENT_CHANGE, |
|
||||||
REPORT_FORWARD_CHANGE, |
|
||||||
} from 'flavours/glitch/actions/reports'; |
|
||||||
import { |
|
||||||
TIMELINE_DELETE, |
|
||||||
} from 'flavours/glitch/actions/timelines'; |
|
||||||
import { Map as ImmutableMap, Set as ImmutableSet } from 'immutable'; |
|
||||||
|
|
||||||
const initialState = ImmutableMap({ |
|
||||||
new: ImmutableMap({ |
|
||||||
isSubmitting: false, |
|
||||||
account_id: null, |
|
||||||
status_ids: ImmutableSet(), |
|
||||||
comment: '', |
|
||||||
forward: false, |
|
||||||
}), |
|
||||||
}); |
|
||||||
|
|
||||||
const deleteStatus = (state, id, references) => { |
|
||||||
references.forEach(ref => { |
|
||||||
state = deleteStatus(state, ref[0], []); |
|
||||||
}); |
|
||||||
|
|
||||||
return state.updateIn(['new', 'status_ids'], ImmutableSet(), set => set.remove(id)); |
|
||||||
}; |
|
||||||
|
|
||||||
export default function reports(state = initialState, action) { |
|
||||||
switch(action.type) { |
|
||||||
case REPORT_INIT: |
|
||||||
return state.withMutations(map => { |
|
||||||
map.setIn(['new', 'isSubmitting'], false); |
|
||||||
map.setIn(['new', 'account_id'], action.account.get('id')); |
|
||||||
|
|
||||||
if (state.getIn(['new', 'account_id']) !== action.account.get('id')) { |
|
||||||
map.setIn(['new', 'status_ids'], action.status ? ImmutableSet([action.status.getIn(['reblog', 'id'], action.status.get('id'))]) : ImmutableSet()); |
|
||||||
map.setIn(['new', 'comment'], ''); |
|
||||||
} else if (action.status) { |
|
||||||
map.updateIn(['new', 'status_ids'], ImmutableSet(), set => set.add(action.status.getIn(['reblog', 'id'], action.status.get('id')))); |
|
||||||
} |
|
||||||
}); |
|
||||||
case REPORT_STATUS_TOGGLE: |
|
||||||
return state.updateIn(['new', 'status_ids'], ImmutableSet(), set => { |
|
||||||
if (action.checked) { |
|
||||||
return set.add(action.statusId); |
|
||||||
} |
|
||||||
|
|
||||||
return set.remove(action.statusId); |
|
||||||
}); |
|
||||||
case REPORT_COMMENT_CHANGE: |
|
||||||
return state.setIn(['new', 'comment'], action.comment); |
|
||||||
case REPORT_FORWARD_CHANGE: |
|
||||||
return state.setIn(['new', 'forward'], action.forward); |
|
||||||
case REPORT_SUBMIT_REQUEST: |
|
||||||
return state.setIn(['new', 'isSubmitting'], true); |
|
||||||
case REPORT_SUBMIT_FAIL: |
|
||||||
return state.setIn(['new', 'isSubmitting'], false); |
|
||||||
case REPORT_CANCEL: |
|
||||||
case REPORT_SUBMIT_SUCCESS: |
|
||||||
return state.withMutations(map => { |
|
||||||
map.setIn(['new', 'account_id'], null); |
|
||||||
map.setIn(['new', 'status_ids'], ImmutableSet()); |
|
||||||
map.setIn(['new', 'comment'], ''); |
|
||||||
map.setIn(['new', 'isSubmitting'], false); |
|
||||||
}); |
|
||||||
case TIMELINE_DELETE: |
|
||||||
return deleteStatus(state, action.id, action.references); |
|
||||||
default: |
|
||||||
return state; |
|
||||||
} |
|
||||||
}; |
|
@ -0,0 +1,13 @@ |
|||||||
|
import { RULES_FETCH_SUCCESS } from 'flavours/glitch/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; |
||||||
|
} |
||||||
|
} |
@ -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) { |
})); |
||||||
return dispatch => { |
|
||||||
dispatch({ |
export const submitReport = (params, onSuccess, onFail) => (dispatch, getState) => { |
||||||
type: REPORT_INIT, |
dispatch(submitReportRequest()); |
||||||
account, |
|
||||||
status, |
api(getState).post('/api/v1/reports', params).then(response => { |
||||||
}); |
dispatch(submitReportSuccess(response.data)); |
||||||
|
if (onSuccess) onSuccess(); |
||||||
dispatch(openModal('REPORT')); |
}).catch(error => { |
||||||
}; |
dispatch(submitReportFail(error)); |
||||||
}; |
if (onFail) onFail(); |
||||||
|
}); |
||||||
export function cancelReport() { |
}; |
||||||
return { |
|
||||||
type: REPORT_CANCEL, |
export const submitReportRequest = () => ({ |
||||||
}; |
type: REPORT_SUBMIT_REQUEST, |
||||||
}; |
}); |
||||||
|
|
||||||
export function toggleStatusReport(statusId, checked) { |
export const submitReportSuccess = report => ({ |
||||||
return { |
type: REPORT_SUBMIT_SUCCESS, |
||||||
type: REPORT_STATUS_TOGGLE, |
report, |
||||||
statusId, |
}); |
||||||
checked, |
|
||||||
}; |
export const submitReportFail = error => ({ |
||||||
}; |
type: REPORT_SUBMIT_FAIL, |
||||||
|
error, |
||||||
export function submitReport() { |
}); |
||||||
return (dispatch, getState) => { |
|
||||||
dispatch(submitReportRequest()); |
|
||||||
|
|
||||||
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, |
|
||||||
}; |
|
||||||
}; |
|
||||||
|
@ -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, |
||||||
|
}); |
@ -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; |
@ -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> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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,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); |
|
||||||
|
@ -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> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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,64 +0,0 @@ |
|||||||
import { |
|
||||||
REPORT_INIT, |
|
||||||
REPORT_SUBMIT_REQUEST, |
|
||||||
REPORT_SUBMIT_SUCCESS, |
|
||||||
REPORT_SUBMIT_FAIL, |
|
||||||
REPORT_CANCEL, |
|
||||||
REPORT_STATUS_TOGGLE, |
|
||||||
REPORT_COMMENT_CHANGE, |
|
||||||
REPORT_FORWARD_CHANGE, |
|
||||||
} from '../actions/reports'; |
|
||||||
import { Map as ImmutableMap, Set as ImmutableSet } from 'immutable'; |
|
||||||
|
|
||||||
const initialState = ImmutableMap({ |
|
||||||
new: ImmutableMap({ |
|
||||||
isSubmitting: false, |
|
||||||
account_id: null, |
|
||||||
status_ids: ImmutableSet(), |
|
||||||
comment: '', |
|
||||||
forward: false, |
|
||||||
}), |
|
||||||
}); |
|
||||||
|
|
||||||
export default function reports(state = initialState, action) { |
|
||||||
switch(action.type) { |
|
||||||
case REPORT_INIT: |
|
||||||
return state.withMutations(map => { |
|
||||||
map.setIn(['new', 'isSubmitting'], false); |
|
||||||
map.setIn(['new', 'account_id'], action.account.get('id')); |
|
||||||
|
|
||||||
if (state.getIn(['new', 'account_id']) !== action.account.get('id')) { |
|
||||||
map.setIn(['new', 'status_ids'], action.status ? ImmutableSet([action.status.getIn(['reblog', 'id'], action.status.get('id'))]) : ImmutableSet()); |
|
||||||
map.setIn(['new', 'comment'], ''); |
|
||||||
} else if (action.status) { |
|
||||||
map.updateIn(['new', 'status_ids'], ImmutableSet(), set => set.add(action.status.getIn(['reblog', 'id'], action.status.get('id')))); |
|
||||||
} |
|
||||||
}); |
|
||||||
case REPORT_STATUS_TOGGLE: |
|
||||||
return state.updateIn(['new', 'status_ids'], ImmutableSet(), set => { |
|
||||||
if (action.checked) { |
|
||||||
return set.add(action.statusId); |
|
||||||
} |
|
||||||
|
|
||||||
return set.remove(action.statusId); |
|
||||||
}); |
|
||||||
case REPORT_COMMENT_CHANGE: |
|
||||||
return state.setIn(['new', 'comment'], action.comment); |
|
||||||
case REPORT_FORWARD_CHANGE: |
|
||||||
return state.setIn(['new', 'forward'], action.forward); |
|
||||||
case REPORT_SUBMIT_REQUEST: |
|
||||||
return state.setIn(['new', 'isSubmitting'], true); |
|
||||||
case REPORT_SUBMIT_FAIL: |
|
||||||
return state.setIn(['new', 'isSubmitting'], false); |
|
||||||
case REPORT_CANCEL: |
|
||||||
case REPORT_SUBMIT_SUCCESS: |
|
||||||
return state.withMutations(map => { |
|
||||||
map.setIn(['new', 'account_id'], null); |
|
||||||
map.setIn(['new', 'status_ids'], ImmutableSet()); |
|
||||||
map.setIn(['new', 'comment'], ''); |
|
||||||
map.setIn(['new', 'isSubmitting'], false); |
|
||||||
}); |
|
||||||
default: |
|
||||||
return state; |
|
||||||
} |
|
||||||
}; |
|
@ -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; |
||||||
|
} |
||||||
|
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue