Turn report screen into a modal (#3965)

This commit is contained in:
Eugen Rochko 2017-06-27 18:07:21 +02:00 committed by GitHub
parent 16d0aed403
commit 12e7c81dd8
10 changed files with 98 additions and 125 deletions

View file

@ -1,4 +1,5 @@
import api from '../api'; import api from '../api';
import { openModal, closeModal } from './modal';
export const REPORT_INIT = 'REPORT_INIT'; export const REPORT_INIT = 'REPORT_INIT';
export const REPORT_CANCEL = 'REPORT_CANCEL'; export const REPORT_CANCEL = 'REPORT_CANCEL';
@ -11,10 +12,14 @@ export const REPORT_STATUS_TOGGLE = 'REPORT_STATUS_TOGGLE';
export const REPORT_COMMENT_CHANGE = 'REPORT_COMMENT_CHANGE'; export const REPORT_COMMENT_CHANGE = 'REPORT_COMMENT_CHANGE';
export function initReport(account, status) { export function initReport(account, status) {
return { return dispatch => {
dispatch({
type: REPORT_INIT, type: REPORT_INIT,
account, account,
status, status,
});
dispatch(openModal('REPORT'));
}; };
}; };
@ -40,7 +45,10 @@ export function submitReport() {
account_id: getState().getIn(['reports', 'new', 'account_id']), account_id: getState().getIn(['reports', 'new', 'account_id']),
status_ids: getState().getIn(['reports', 'new', 'status_ids']), status_ids: getState().getIn(['reports', 'new', 'status_ids']),
comment: getState().getIn(['reports', 'new', 'comment']), comment: getState().getIn(['reports', 'new', 'comment']),
}).then(response => dispatch(submitReportSuccess(response.data))).catch(error => dispatch(submitReportFail(error))); }).then(response => {
dispatch(closeModal());
dispatch(submitReportSuccess(response.data));
}).catch(error => dispatch(submitReportFail(error)));
}; };
}; };

View file

@ -87,7 +87,6 @@ export default class StatusActionBar extends ImmutablePureComponent {
handleReport = () => { handleReport = () => {
this.props.onReport(this.props.status); this.props.onReport(this.props.status);
this.context.router.history.push('/report');
} }
handleConversationMuteClick = () => { handleConversationMuteClick = () => {

View file

@ -38,7 +38,6 @@ export default class Header extends ImmutablePureComponent {
handleReport = () => { handleReport = () => {
this.props.onReport(this.props.account); this.props.onReport(this.props.account);
this.context.router.history.push('/report');
} }
handleMute = () => { handleMute = () => {

View file

@ -56,7 +56,6 @@ export default class ActionBar extends React.PureComponent {
handleReport = () => { handleReport = () => {
this.props.onReport(this.props.status); this.props.onReport(this.props.status);
this.context.router.history.push('/report');
} }
render () { render () {

View file

@ -5,6 +5,7 @@ import OnboardingModal from './onboarding_modal';
import VideoModal from './video_modal'; import VideoModal from './video_modal';
import BoostModal from './boost_modal'; import BoostModal from './boost_modal';
import ConfirmationModal from './confirmation_modal'; import ConfirmationModal from './confirmation_modal';
import ReportModal from './report_modal';
import TransitionMotion from 'react-motion/lib/TransitionMotion'; import TransitionMotion from 'react-motion/lib/TransitionMotion';
import spring from 'react-motion/lib/spring'; import spring from 'react-motion/lib/spring';
@ -14,6 +15,7 @@ const MODAL_COMPONENTS = {
'VIDEO': VideoModal, 'VIDEO': VideoModal,
'BOOST': BoostModal, 'BOOST': BoostModal,
'CONFIRM': ConfirmationModal, 'CONFIRM': ConfirmationModal,
'REPORT': ReportModal,
}; };
export default class ModalRoot extends React.PureComponent { export default class ModalRoot extends React.PureComponent {

View file

@ -1,19 +1,17 @@
import React from 'react'; import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { changeReportComment, submitReport } from '../../actions/reports'; import { changeReportComment, submitReport } from '../../../actions/reports';
import { refreshAccountTimeline } from '../../actions/timelines'; import { refreshAccountTimeline } from '../../../actions/timelines';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import Column from '../ui/components/column'; import { makeGetAccount } from '../../../selectors';
import Button from '../../components/button';
import { makeGetAccount } from '../../selectors';
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl'; import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
import StatusCheckBox from './containers/status_check_box_container'; import StatusCheckBox from '../../report/containers/status_check_box_container';
import Immutable from 'immutable'; import Immutable from 'immutable';
import ColumnBackButtonSlim from '../../components/column_back_button_slim'; import ImmutablePureComponent from 'react-immutable-pure-component';
import Button from '../../../components/button';
const messages = defineMessages({ const messages = defineMessages({
heading: { id: 'report.heading', defaultMessage: 'New report' },
placeholder: { id: 'report.placeholder', defaultMessage: 'Additional comments' }, placeholder: { id: 'report.placeholder', defaultMessage: 'Additional comments' },
submit: { id: 'report.submit', defaultMessage: 'Submit' }, submit: { id: 'report.submit', defaultMessage: 'Submit' },
}); });
@ -37,11 +35,7 @@ const makeMapStateToProps = () => {
@connect(makeMapStateToProps) @connect(makeMapStateToProps)
@injectIntl @injectIntl
export default class Report extends React.PureComponent { export default class ReportModal extends ImmutablePureComponent {
static contextTypes = {
router: PropTypes.object,
};
static propTypes = { static propTypes = {
isSubmitting: PropTypes.bool, isSubmitting: PropTypes.bool,
@ -52,17 +46,15 @@ export default class Report extends React.PureComponent {
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
}; };
componentWillMount () { handleCommentChange = (e) => {
if (!this.props.account) { this.props.dispatch(changeReportComment(e.target.value));
this.context.router.history.replace('/');
} }
handleSubmit = () => {
this.props.dispatch(submitReport());
} }
componentDidMount () { componentDidMount () {
if (!this.props.account) {
return;
}
this.props.dispatch(refreshAccountTimeline(this.props.account.get('id'))); this.props.dispatch(refreshAccountTimeline(this.props.account.get('id')));
} }
@ -72,15 +64,6 @@ export default class Report extends React.PureComponent {
} }
} }
handleCommentChange = (e) => {
this.props.dispatch(changeReportComment(e.target.value));
}
handleSubmit = () => {
this.props.dispatch(submitReport());
this.context.router.history.replace('/');
}
render () { render () {
const { account, comment, intl, statusIds, isSubmitting } = this.props; const { account, comment, intl, statusIds, isSubmitting } = this.props;
@ -89,36 +72,33 @@ export default class Report extends React.PureComponent {
} }
return ( return (
<Column heading={intl.formatMessage(messages.heading)} icon='flag'> <div className='modal-root__modal report-modal'>
<ColumnBackButtonSlim /> <div className='report-modal__target'>
<FormattedMessage id='report.target' defaultMessage='Report {target}' values={{ target: <strong>{account.get('acct')}</strong> }} />
<div className='report scrollable'>
<div className='report__target'>
<FormattedMessage id='report.target' defaultMessage='Reporting' />
<strong>{account.get('acct')}</strong>
</div> </div>
<div className='scrollable report__statuses'> <div className='report-modal__container'>
<div className='report-modal__statuses'>
<div> <div>
{statusIds.map(statusId => <StatusCheckBox id={statusId} key={statusId} disabled={isSubmitting} />)} {statusIds.map(statusId => <StatusCheckBox id={statusId} key={statusId} disabled={isSubmitting} />)}
</div> </div>
</div> </div>
<div className='report__textarea-wrapper'> <div className='report-modal__comment'>
<textarea <textarea
className='report__textarea' className='setting-text light'
placeholder={intl.formatMessage(messages.placeholder)} placeholder={intl.formatMessage(messages.placeholder)}
value={comment} value={comment}
onChange={this.handleCommentChange} onChange={this.handleCommentChange}
disabled={isSubmitting} disabled={isSubmitting}
/> />
</div>
</div>
<div className='report__submit'> <div className='report-modal__action-bar'>
<div className='report__submit-button'><Button disabled={isSubmitting} text={intl.formatMessage(messages.submit)} onClick={this.handleSubmit} /></div> <Button disabled={isSubmitting} text={intl.formatMessage(messages.submit)} onClick={this.handleSubmit} />
</div> </div>
</div> </div>
</div>
</Column>
); );
} }

View file

@ -15,7 +15,6 @@ import { refreshHomeTimeline } from '../../actions/timelines';
import { refreshNotifications } from '../../actions/notifications'; import { refreshNotifications } from '../../actions/notifications';
import UploadArea from './components/upload_area'; import UploadArea from './components/upload_area';
import ColumnsAreaContainer from './containers/columns_area_container'; import ColumnsAreaContainer from './containers/columns_area_container';
import Status from '../../features/status'; import Status from '../../features/status';
import GettingStarted from '../../features/getting_started'; import GettingStarted from '../../features/getting_started';
import PublicTimeline from '../../features/public_timeline'; import PublicTimeline from '../../features/public_timeline';
@ -35,7 +34,6 @@ import GenericNotFound from '../../features/generic_not_found';
import FavouritedStatuses from '../../features/favourited_statuses'; import FavouritedStatuses from '../../features/favourited_statuses';
import Blocks from '../../features/blocks'; import Blocks from '../../features/blocks';
import Mutes from '../../features/mutes'; import Mutes from '../../features/mutes';
import Report from '../../features/report';
// Small wrapper to pass multiColumn to the route components // Small wrapper to pass multiColumn to the route components
const WrappedSwitch = ({ multiColumn, children }) => ( const WrappedSwitch = ({ multiColumn, children }) => (
@ -206,7 +204,6 @@ export default class UI extends React.PureComponent {
<WrappedRoute path='/follow_requests' component={FollowRequests} content={children} /> <WrappedRoute path='/follow_requests' component={FollowRequests} content={children} />
<WrappedRoute path='/blocks' component={Blocks} content={children} /> <WrappedRoute path='/blocks' component={Blocks} content={children} />
<WrappedRoute path='/mutes' component={Mutes} content={children} /> <WrappedRoute path='/mutes' component={Mutes} content={children} />
<WrappedRoute path='/report' component={Report} content={children} />
<WrappedRoute component={GenericNotFound} content={children} /> <WrappedRoute component={GenericNotFound} content={children} />
</WrappedSwitch> </WrappedSwitch>

View file

@ -1127,6 +1127,23 @@
], ],
"path": "app/javascript/mastodon/features/ui/components/onboarding_modal.json" "path": "app/javascript/mastodon/features/ui/components/onboarding_modal.json"
}, },
{
"descriptors": [
{
"defaultMessage": "Additional comments",
"id": "report.placeholder"
},
{
"defaultMessage": "Submit",
"id": "report.submit"
},
{
"defaultMessage": "Report {target}",
"id": "report.target"
}
],
"path": "app/javascript/mastodon/features/ui/components/report_modal.json"
},
{ {
"descriptors": [ "descriptors": [
{ {

View file

@ -136,10 +136,10 @@
"privacy.unlisted.long": "Do not post to public timelines", "privacy.unlisted.long": "Do not post to public timelines",
"privacy.unlisted.short": "Unlisted", "privacy.unlisted.short": "Unlisted",
"reply_indicator.cancel": "Cancel", "reply_indicator.cancel": "Cancel",
"report.heading": "New report", "report.heading": "Report {target}",
"report.placeholder": "Additional comments", "report.placeholder": "Additional comments",
"report.submit": "Submit", "report.submit": "Submit",
"report.target": "Reporting", "report.target": "Reporting {target}",
"search.placeholder": "Search", "search.placeholder": "Search",
"search_results.total": "{count, number} {count, plural, one {result} other {results}}", "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
"status.cannot_reblog": "This post cannot be boosted", "status.cannot_reblog": "This post cannot be boosted",

View file

@ -600,13 +600,15 @@
} }
.status-check-box { .status-check-box {
border-bottom: 1px solid lighten($ui-base-color, 8%); border-bottom: 1px solid $ui-secondary-color;
display: flex; display: flex;
.status__content { .status__content {
background: lighten($ui-base-color, 4%);
flex: 1 1 auto; flex: 1 1 auto;
padding: 10px; padding: 10px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} }
} }
@ -1830,6 +1832,17 @@
@media screen and (max-width: 600px) { @media screen and (max-width: 600px) {
font-size: 16px; font-size: 16px;
} }
&.light {
color: $ui-base-color;
border-bottom: 2px solid lighten($ui-base-color, 27%);
&:focus,
&:active {
color: $ui-base-color;
border-bottom-color: $ui-highlight-color;
}
}
} }
@import 'boost'; @import 'boost';
@ -2287,67 +2300,6 @@ button.icon-button.active i.fa-retweet {
vertical-align: middle; vertical-align: middle;
} }
.report.scrollable {
box-sizing: border-box;
display: flex;
flex-direction: column;
max-height: 100%;
}
.report__target {
border-bottom: 1px solid lighten($ui-base-color, 4%);
color: $ui-secondary-color;
flex: 0 0 auto;
padding: 10px;
strong {
display: block;
color: $primary-text-color;
font-weight: 500;
}
}
.report__statuses {
flex: 1 1 auto;
}
.report__textarea-wrapper {
flex: 0 0 100px;
padding: 10px;
}
.report__textarea {
background: transparent;
box-sizing: border-box;
border: 0;
border-bottom: 2px solid $ui-primary-color;
border-radius: 2px 2px 0 0;
color: $primary-text-color;
display: block;
font-family: inherit;
font-size: 14px;
margin-bottom: 10px;
outline: 0;
padding: 7px 4px;
resize: vertical;
width: 100%;
&:active,
&:focus {
border-bottom-color: $ui-highlight-color;
background: rgba($base-overlay-background, 0.1);
}
}
.report__submit {
margin-top: 10px;
overflow: hidden;
}
.report__submit-button {
float: right;
}
.empty-column-indicator { .empty-column-indicator {
color: lighten($ui-base-color, 20%); color: lighten($ui-base-color, 20%);
background: $ui-base-color; background: $ui-base-color;
@ -3245,7 +3197,8 @@ button.icon-button.active i.fa-retweet {
} }
.boost-modal, .boost-modal,
.confirmation-modal { .confirmation-modal,
.report-modal {
background: lighten($ui-secondary-color, 8%); background: lighten($ui-secondary-color, 8%);
color: $ui-base-color; color: $ui-base-color;
border-radius: 8px; border-radius: 8px;
@ -3281,7 +3234,8 @@ button.icon-button.active i.fa-retweet {
} }
.boost-modal__action-bar, .boost-modal__action-bar,
.confirmation-modal__action-bar { .confirmation-modal__action-bar,
.report-modal__action-bar {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
background: $ui-secondary-color; background: $ui-secondary-color;
@ -3317,6 +3271,23 @@ button.icon-button.active i.fa-retweet {
} }
} }
.report-modal__statuses,
.report-modal__comment {
padding: 10px;
}
.report-modal__statuses {
min-height: 20vh;
overflow-y: auto;
overflow-x: hidden;
}
.report-modal__comment {
.setting-text {
margin-top: 10px;
}
}
.confirmation-modal__action-bar { .confirmation-modal__action-bar {
.confirmation-modal__cancel-button { .confirmation-modal__cancel-button {
background-color: transparent; background-color: transparent;
@ -3332,7 +3303,8 @@ button.icon-button.active i.fa-retweet {
} }
} }
.confirmation-modal__container { .confirmation-modal__container,
.report-modal__target {
padding: 30px; padding: 30px;
font-size: 16px; font-size: 16px;
text-align: center; text-align: center;