logo

mastofe

My custom branche(s) on git.pleroma.social/pleroma/mastofe
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:

Mapp/javascript/mastodon/actions/reports.js18+++++++++++++-----
Mapp/javascript/mastodon/components/status_action_bar.js1-
Mapp/javascript/mastodon/features/account_timeline/components/header.js1-
Dapp/javascript/mastodon/features/report/index.js125-------------------------------------------------------------------------------
Mapp/javascript/mastodon/features/status/components/action_bar.js1-
Mapp/javascript/mastodon/features/ui/components/modal_root.js2++
Aapp/javascript/mastodon/features/ui/components/report_modal.js105+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mapp/javascript/mastodon/features/ui/index.js3---
Mapp/javascript/mastodon/locales/defaultMessages.json17+++++++++++++++++
Mapp/javascript/mastodon/locales/en.json4++--
Mapp/javascript/styles/components.scss104+++++++++++++++++++++++++++++--------------------------------------------------
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;