commit: 12e7c81dd8739a0f83513054c0fda22e098e2458
parent: 16d0aed403485e5a98afd9cbf7e35bb929443731
Author: Eugen Rochko <eugen@zeonfederated.com>
Date: Tue, 27 Jun 2017 18:07:21 +0200
Turn report screen into a modal (#3965)
Diffstat:
11 files changed, 177 insertions(+), 204 deletions(-)
diff --git a/app/javascript/mastodon/actions/reports.js b/app/javascript/mastodon/actions/reports.js
@@ -1,4 +1,5 @@
import api from '../api';
+import { openModal, closeModal } from './modal';
export const REPORT_INIT = 'REPORT_INIT';
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 function initReport(account, status) {
- return {
- type: REPORT_INIT,
- account,
- status,
+ return dispatch => {
+ dispatch({
+ type: REPORT_INIT,
+ account,
+ status,
+ });
+
+ dispatch(openModal('REPORT'));
};
};
@@ -40,7 +45,10 @@ export function submitReport() {
account_id: getState().getIn(['reports', 'new', 'account_id']),
status_ids: getState().getIn(['reports', 'new', 'status_ids']),
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)));
};
};
diff --git a/app/javascript/mastodon/components/status_action_bar.js b/app/javascript/mastodon/components/status_action_bar.js
@@ -87,7 +87,6 @@ export default class StatusActionBar extends ImmutablePureComponent {
handleReport = () => {
this.props.onReport(this.props.status);
- this.context.router.history.push('/report');
}
handleConversationMuteClick = () => {
diff --git a/app/javascript/mastodon/features/account_timeline/components/header.js b/app/javascript/mastodon/features/account_timeline/components/header.js
@@ -38,7 +38,6 @@ export default class Header extends ImmutablePureComponent {
handleReport = () => {
this.props.onReport(this.props.account);
- this.context.router.history.push('/report');
}
handleMute = () => {
diff --git a/app/javascript/mastodon/features/report/index.js b/app/javascript/mastodon/features/report/index.js
@@ -1,125 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import { changeReportComment, submitReport } from '../../actions/reports';
-import { refreshAccountTimeline } from '../../actions/timelines';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import Column from '../ui/components/column';
-import Button from '../../components/button';
-import { makeGetAccount } from '../../selectors';
-import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
-import StatusCheckBox from './containers/status_check_box_container';
-import Immutable from 'immutable';
-import ColumnBackButtonSlim from '../../components/column_back_button_slim';
-
-const messages = defineMessages({
- heading: { id: 'report.heading', defaultMessage: 'New report' },
- placeholder: { id: 'report.placeholder', defaultMessage: 'Additional comments' },
- submit: { id: 'report.submit', defaultMessage: 'Submit' },
-});
-
-const makeMapStateToProps = () => {
- const getAccount = makeGetAccount();
-
- const mapStateToProps = state => {
- const accountId = state.getIn(['reports', 'new', 'account_id']);
-
- return {
- isSubmitting: state.getIn(['reports', 'new', 'isSubmitting']),
- account: getAccount(state, accountId),
- comment: state.getIn(['reports', 'new', 'comment']),
- statusIds: Immutable.OrderedSet(state.getIn(['timelines', `account:${accountId}`, 'items'])).union(state.getIn(['reports', 'new', 'status_ids'])),
- };
- };
-
- return mapStateToProps;
-};
-
-@connect(makeMapStateToProps)
-@injectIntl
-export default class Report extends React.PureComponent {
-
- static contextTypes = {
- router: PropTypes.object,
- };
-
- static propTypes = {
- isSubmitting: PropTypes.bool,
- account: ImmutablePropTypes.map,
- statusIds: ImmutablePropTypes.orderedSet.isRequired,
- comment: PropTypes.string.isRequired,
- dispatch: PropTypes.func.isRequired,
- intl: PropTypes.object.isRequired,
- };
-
- componentWillMount () {
- if (!this.props.account) {
- this.context.router.history.replace('/');
- }
- }
-
- componentDidMount () {
- if (!this.props.account) {
- return;
- }
-
- this.props.dispatch(refreshAccountTimeline(this.props.account.get('id')));
- }
-
- componentWillReceiveProps (nextProps) {
- if (this.props.account !== nextProps.account && nextProps.account) {
- this.props.dispatch(refreshAccountTimeline(nextProps.account.get('id')));
- }
- }
-
- handleCommentChange = (e) => {
- this.props.dispatch(changeReportComment(e.target.value));
- }
-
- handleSubmit = () => {
- this.props.dispatch(submitReport());
- this.context.router.history.replace('/');
- }
-
- render () {
- const { account, comment, intl, statusIds, isSubmitting } = this.props;
-
- if (!account) {
- return null;
- }
-
- return (
- <Column heading={intl.formatMessage(messages.heading)} icon='flag'>
- <ColumnBackButtonSlim />
-
- <div className='report scrollable'>
- <div className='report__target'>
- <FormattedMessage id='report.target' defaultMessage='Reporting' />
- <strong>{account.get('acct')}</strong>
- </div>
-
- <div className='scrollable report__statuses'>
- <div>
- {statusIds.map(statusId => <StatusCheckBox id={statusId} key={statusId} disabled={isSubmitting} />)}
- </div>
- </div>
-
- <div className='report__textarea-wrapper'>
- <textarea
- className='report__textarea'
- placeholder={intl.formatMessage(messages.placeholder)}
- value={comment}
- onChange={this.handleCommentChange}
- disabled={isSubmitting}
- />
-
- <div className='report__submit'>
- <div className='report__submit-button'><Button disabled={isSubmitting} text={intl.formatMessage(messages.submit)} onClick={this.handleSubmit} /></div>
- </div>
- </div>
- </div>
- </Column>
- );
- }
-
-}
diff --git a/app/javascript/mastodon/features/status/components/action_bar.js b/app/javascript/mastodon/features/status/components/action_bar.js
@@ -56,7 +56,6 @@ export default class ActionBar extends React.PureComponent {
handleReport = () => {
this.props.onReport(this.props.status);
- this.context.router.history.push('/report');
}
render () {
diff --git a/app/javascript/mastodon/features/ui/components/modal_root.js b/app/javascript/mastodon/features/ui/components/modal_root.js
@@ -5,6 +5,7 @@ import OnboardingModal from './onboarding_modal';
import VideoModal from './video_modal';
import BoostModal from './boost_modal';
import ConfirmationModal from './confirmation_modal';
+import ReportModal from './report_modal';
import TransitionMotion from 'react-motion/lib/TransitionMotion';
import spring from 'react-motion/lib/spring';
@@ -14,6 +15,7 @@ const MODAL_COMPONENTS = {
'VIDEO': VideoModal,
'BOOST': BoostModal,
'CONFIRM': ConfirmationModal,
+ 'REPORT': ReportModal,
};
export default class ModalRoot extends React.PureComponent {
diff --git a/app/javascript/mastodon/features/ui/components/report_modal.js b/app/javascript/mastodon/features/ui/components/report_modal.js
@@ -0,0 +1,105 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { changeReportComment, submitReport } from '../../../actions/reports';
+import { refreshAccountTimeline } from '../../../actions/timelines';
+import PropTypes from 'prop-types';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import { makeGetAccount } from '../../../selectors';
+import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
+import StatusCheckBox from '../../report/containers/status_check_box_container';
+import Immutable from 'immutable';
+import ImmutablePureComponent from 'react-immutable-pure-component';
+import Button from '../../../components/button';
+
+const messages = defineMessages({
+ placeholder: { id: 'report.placeholder', defaultMessage: 'Additional comments' },
+ submit: { id: 'report.submit', defaultMessage: 'Submit' },
+});
+
+const makeMapStateToProps = () => {
+ const getAccount = makeGetAccount();
+
+ const mapStateToProps = state => {
+ const accountId = state.getIn(['reports', 'new', 'account_id']);
+
+ return {
+ isSubmitting: state.getIn(['reports', 'new', 'isSubmitting']),
+ account: getAccount(state, accountId),
+ comment: state.getIn(['reports', 'new', 'comment']),
+ statusIds: Immutable.OrderedSet(state.getIn(['timelines', `account:${accountId}`, 'items'])).union(state.getIn(['reports', 'new', 'status_ids'])),
+ };
+ };
+
+ return mapStateToProps;
+};
+
+@connect(makeMapStateToProps)
+@injectIntl
+export default class ReportModal extends ImmutablePureComponent {
+
+ static propTypes = {
+ isSubmitting: PropTypes.bool,
+ account: ImmutablePropTypes.map,
+ statusIds: ImmutablePropTypes.orderedSet.isRequired,
+ comment: PropTypes.string.isRequired,
+ dispatch: PropTypes.func.isRequired,
+ intl: PropTypes.object.isRequired,
+ };
+
+ handleCommentChange = (e) => {
+ this.props.dispatch(changeReportComment(e.target.value));
+ }
+
+ handleSubmit = () => {
+ this.props.dispatch(submitReport());
+ }
+
+ componentDidMount () {
+ this.props.dispatch(refreshAccountTimeline(this.props.account.get('id')));
+ }
+
+ componentWillReceiveProps (nextProps) {
+ if (this.props.account !== nextProps.account && nextProps.account) {
+ this.props.dispatch(refreshAccountTimeline(nextProps.account.get('id')));
+ }
+ }
+
+ render () {
+ const { account, comment, intl, statusIds, isSubmitting } = this.props;
+
+ if (!account) {
+ return null;
+ }
+
+ return (
+ <div className='modal-root__modal report-modal'>
+ <div className='report-modal__target'>
+ <FormattedMessage id='report.target' defaultMessage='Report {target}' values={{ target: <strong>{account.get('acct')}</strong> }} />
+ </div>
+
+ <div className='report-modal__container'>
+ <div className='report-modal__statuses'>
+ <div>
+ {statusIds.map(statusId => <StatusCheckBox id={statusId} key={statusId} disabled={isSubmitting} />)}
+ </div>
+ </div>
+
+ <div className='report-modal__comment'>
+ <textarea
+ className='setting-text light'
+ placeholder={intl.formatMessage(messages.placeholder)}
+ value={comment}
+ onChange={this.handleCommentChange}
+ disabled={isSubmitting}
+ />
+ </div>
+ </div>
+
+ <div className='report-modal__action-bar'>
+ <Button disabled={isSubmitting} text={intl.formatMessage(messages.submit)} onClick={this.handleSubmit} />
+ </div>
+ </div>
+ );
+ }
+
+}
diff --git a/app/javascript/mastodon/features/ui/index.js b/app/javascript/mastodon/features/ui/index.js
@@ -15,7 +15,6 @@ import { refreshHomeTimeline } from '../../actions/timelines';
import { refreshNotifications } from '../../actions/notifications';
import UploadArea from './components/upload_area';
import ColumnsAreaContainer from './containers/columns_area_container';
-
import Status from '../../features/status';
import GettingStarted from '../../features/getting_started';
import PublicTimeline from '../../features/public_timeline';
@@ -35,7 +34,6 @@ import GenericNotFound from '../../features/generic_not_found';
import FavouritedStatuses from '../../features/favourited_statuses';
import Blocks from '../../features/blocks';
import Mutes from '../../features/mutes';
-import Report from '../../features/report';
// Small wrapper to pass multiColumn to the route components
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='/blocks' component={Blocks} content={children} />
<WrappedRoute path='/mutes' component={Mutes} content={children} />
- <WrappedRoute path='/report' component={Report} content={children} />
<WrappedRoute component={GenericNotFound} content={children} />
</WrappedSwitch>
diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json
@@ -1130,6 +1130,23 @@
{
"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": [
+ {
"defaultMessage": "Compose",
"id": "tabs_bar.compose"
},
diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json
@@ -136,10 +136,10 @@
"privacy.unlisted.long": "Do not post to public timelines",
"privacy.unlisted.short": "Unlisted",
"reply_indicator.cancel": "Cancel",
- "report.heading": "New report",
+ "report.heading": "Report {target}",
"report.placeholder": "Additional comments",
"report.submit": "Submit",
- "report.target": "Reporting",
+ "report.target": "Reporting {target}",
"search.placeholder": "Search",
"search_results.total": "{count, number} {count, plural, one {result} other {results}}",
"status.cannot_reblog": "This post cannot be boosted",
diff --git a/app/javascript/styles/components.scss b/app/javascript/styles/components.scss
@@ -600,13 +600,15 @@
}
.status-check-box {
- border-bottom: 1px solid lighten($ui-base-color, 8%);
+ border-bottom: 1px solid $ui-secondary-color;
display: flex;
.status__content {
- background: lighten($ui-base-color, 4%);
flex: 1 1 auto;
padding: 10px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
}
}
@@ -1830,6 +1832,17 @@
@media screen and (max-width: 600px) {
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';
@@ -2287,67 +2300,6 @@ button.icon-button.active i.fa-retweet {
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 {
color: lighten($ui-base-color, 20%);
background: $ui-base-color;
@@ -3245,7 +3197,8 @@ button.icon-button.active i.fa-retweet {
}
.boost-modal,
-.confirmation-modal {
+.confirmation-modal,
+.report-modal {
background: lighten($ui-secondary-color, 8%);
color: $ui-base-color;
border-radius: 8px;
@@ -3281,7 +3234,8 @@ button.icon-button.active i.fa-retweet {
}
.boost-modal__action-bar,
-.confirmation-modal__action-bar {
+.confirmation-modal__action-bar,
+.report-modal__action-bar {
display: flex;
justify-content: space-between;
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__cancel-button {
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;
font-size: 16px;
text-align: center;