Refactor report modal

* move all the report modal presentation into the component
* add strings to general l10n file because we don’t handle l10n files in components
* Add current project id and username to the data before submitting
This commit is contained in:
chrisgarrity 2018-05-27 18:00:56 -04:00
parent 34042c9e8e
commit 08cf4f4f3d
3 changed files with 215 additions and 153 deletions

View file

@ -1,34 +1,186 @@
const bindAll = require('lodash.bindall');
const PropTypes = require('prop-types');
const React = require('react');
const FormattedMessage = require('react-intl').FormattedMessage;
const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const Modal = require('../base/modal.jsx');
const log = require('../../../lib/log.js');
const Form = require('../../forms/form.jsx');
const Button = require('../../forms/button.jsx');
const Select = require('../../forms/select.jsx');
const Spinner = require('../../spinner/spinner.jsx');
const TextArea = require('../../forms/textarea.jsx');
require('../../forms/button.scss');
require('./modal.scss');
const ReportModal = props => (
<Modal
className="mod-report"
{...props}
>
<div>
<div className="report-modal-header">
<div className="report-content-label">
{props.contentLabel}
</div>
</div>
class ReportModal extends React.Component {
constructor (props) {
super(props);
bindAll(this, [
'handleReasonSelect',
'handleSubmit'
]);
this.state = {
prompt: props.intl.formatMessage({id: 'report.promptPlaceholder'}),
reason: '',
waiting: false
};
}
handleReasonSelect (name, value) {
const prompts = [
this.props.intl.formatMessage({id: 'report.promptCopy'}),
this.props.intl.formatMessage({id: 'report.promptUncredited'}),
this.props.intl.formatMessage({id: 'report.promptScary'}),
this.props.intl.formatMessage({id: 'report.promptLanguage'}),
this.props.intl.formatMessage({id: 'report.promptMusic'}),
this.props.intl.formatMessage({id: 'report.promptPersonal'}),
this.props.intl.formatMessage({id: 'report.promptGuidelines'}),
'not used',
this.props.intl.formatMessage({id: 'report.promptImage'})
];
this.setState({prompt: prompts[value], reason: value});
}
handleSubmit (formData) {
this.setState({waiting: true});
this.props.onReport(formData, err => {
if (err) log.error(err);
this.setState({
prompt: this.props.intl.formatMessage({id: 'report.promptPlaceholder'}),
reason: '',
waiting: false
});
});
}
render () {
const {
intl,
onReport, // eslint-disable-line no-unused-vars
type,
...modalProps
} = this.props;
const contentLabel = intl.formatMessage({id: `report.${type}`});
return (
<Modal
className="mod-report"
contentLabel={contentLabel}
{...modalProps}
>
<div>
<div className="report-modal-header">
<div className="report-content-label">
{contentLabel}
</div>
</div>
<div className="report-modal-content">
{props.children}
</div>
</div>
</Modal>
);
<div className="report-modal-content">
<FormattedMessage
id={`report.${type}Instructions`}
values={{
CommunityGuidelinesLink: (
<a href="/community_guidelines">
<FormattedMessage id="report.CommunityGuidelinesLinkText" />
</a>
)
}}
/>
<Form
className="report"
onSubmit={this.handleSubmit}
>
<Select
required
name="reason"
options={[
{
value: '',
label: this.props.intl.formatMessage({id: 'report.reasonPlaceHolder'})
},
{
value: '0',
label: this.props.intl.formatMessage({id: 'report.reasonCopy'})
},
{
value: '1',
label: this.props.intl.formatMessage({id: 'report.reasonUncredited'})
},
{
value: '2',
label: this.props.intl.formatMessage({id: 'report.reasonScary'})
},
{
value: '3',
label: this.props.intl.formatMessage({id: 'report.reasonLanguage'})
},
{
value: '4',
label: this.props.intl.formatMessage({id: 'report.reasonMusic'})
},
{
value: '8',
label: this.props.intl.formatMessage({id: 'report.reasonImage'})
},
{
value: '5',
label: this.props.intl.formatMessage({id: 'report.reasonPersonal'})
},
{
value: '6',
label: this.props.intl.formatMessage({id: 'report.reasonOther'})
}
]}
value={this.state.reason}
onChange={this.handleReasonSelect}
/>
<TextArea
required
className="report-text"
name="reportText"
placeholder={this.state.prompt}
validationErrors={{
maxLength: this.props.intl.formatMessage({id: 'report.tooLongError'}),
minLength: this.props.intl.formatMessage({id: 'report.tooShortError'})
}}
validations={{
// TODO find out max and min characters
maxLength: 500,
minLength: 30
}}
/>
{this.state.reportWaiting ? [
<Button
className="submit-button white"
disabled="disabled"
key="submitButton"
type="submit"
>
<Spinner />
</Button>
] : [
<Button
className="submit-button white"
key="submitButton"
type="submit"
>
<FormattedMessage id="report.send" />
</Button>
]}
</Form>
</div>
</div>
</Modal>
);
}
}
ReportModal.propTypes = {
children: PropTypes.node,
contentLabel: PropTypes.string,
onRequestClose: PropTypes.func
intl: intlShape,
onReport: PropTypes.func,
onRequestClose: PropTypes.func,
type: PropTypes.string
};
module.exports = ReportModal;
module.exports = injectIntl(ReportModal);

View file

@ -159,5 +159,29 @@
"registration.welcomeStepPrompt": "To get started, click on the button below.",
"registration.welcomeStepTitle": "Hurray! Welcome to Scratch!",
"thumbnail.by": "by"
"thumbnail.by": "by",
"report.project": "Report Project",
"report.projectInstructions": "From the dropdown below, please select the reason why you feel this project is disrespectful or inappropriate or otherwise breaks the {CommunityGuidelinesLink}.",
"report.CommunityGuidelinesLinkText": "Scratch Community Guidelines",
"report.reasonPlaceHolder": "Select a reason",
"report.reasonCopy": "Exact Copy of Project",
"report.reasonUncredited": "Uses Image/Music Without Credit",
"report.reasonScary": "Too Violent or Scary",
"report.reasonLanguage": "Inappropriate Language",
"report.reasonMusic": "Inappropriate Music",
"report.reasonImage": "Inappropriate Images",
"report.reasonPersonal": "Sharing Personal Contact Information",
"report.reasonOther": "Other",
"report.promptPlaceholder": "Select a reason why above.",
"report.promptCopy": "Please provide a link to the original project",
"report.promptUncredited": "Please provide links to the uncredited content",
"report.promptScary": "Please say why the project is too violent or scary",
"report.promptLanguage": "Please say where the inappropriate language occurs in the project (For example: Notes & Credits, sprite name, project text, etc.)",
"report.promptMusic": "Please say the name of the audio file with the inappropriate music",
"report.promptPersonal": "Please say where the personal contact information is shared (For example: Notes & Credits, sprite name, project text, etc.)",
"report.promptGuidelines": "Please be specific about why this project does not follow our Community Guidelines",
"report.promptImage": "Please say the name of the sprite or the backdrop with the inappropriate image",
"report.tooLongError": "That's too long! Please find a way to shorten your text.",
"report.tooShortError": "That's too short. Please describe in detail what's inappropriate or disrespectful about the project.",
"report.send": "Send"
}

View file

@ -14,11 +14,6 @@ const sessionActions = require('../../redux/session.js');
const decorateText = require('../../lib/decorate-text.jsx');
const FlexRow = require('../../components/flex-row/flex-row.jsx');
const Button = require('../../components/forms/button.jsx');
const Form = require('../../components/forms/form.jsx');
const Input = require('../../components/forms/input.jsx');
const Select = require('../../components/forms/select.jsx');
const Spinner = require('../../components/spinner/spinner.jsx');
const TextArea = require('../../components/forms/textarea.jsx');
const Avatar = require('../../components/avatar/avatar.jsx');
const CappedNumber = require('../../components/cappednumber/cappednumber.jsx');
const ShareBanner = require('../../components/share-banner/share-banner.jsx');
@ -26,7 +21,6 @@ const ThumbnailColumn = require('../../components/thumbnailcolumn/thumbnailcolum
const InplaceInput = require('../../components/forms/inplace-input.jsx');
const ReportModal = require('../../components/modal/report/modal.jsx');
require('./preview.scss');
class PreviewPresentation extends React.Component {
@ -35,13 +29,10 @@ class PreviewPresentation extends React.Component {
bindAll(this, [
'handleReportClick',
'handleReportClose',
'handleReportReasonSelect',
'handleReportSubmit'
]);
this.state = {
reportOpen: false,
reportPrompt: 'Select a reason why above.',
reportReason: ''
reportOpen: false
};
}
handleReportClick (e) {
@ -51,22 +42,16 @@ class PreviewPresentation extends React.Component {
handleReportClose () {
this.setState({reportOpen: false});
}
handleReportReasonSelect (name, value) {
const prompts = [
'Please provide a link to the original project',
'Please provide links to the uncredited content',
'Please say why the project is too violent or scary',
'Please say where the inappropriate language occurs in the project (For example: Notes & Credits, sprite name, project text, etc.)',
'Please say the name of the audio file with the inappropriate music',
'Please say where the personal contact information is shared (For example: Notes & Credits, sprite name, project text, etc.)',
'Please be specific about why this project does not follow our Community Guidelines',
'not used',
'Please say the name of the sprite or the backdrop with the inappropriate image'
];
this.setState({reportPrompt: prompts[value]});
}
handleReportSubmit (formData) {
console.log('submit report data', formData);
handleReportSubmit (formData, callback) {
const data = {
...formData,
id: this.props.projectId,
username: this.props.user.username
};
console.log('submit report data', data);
// TODO: pass error to modal via callback.
callback();
this.setState({reportOpen: false});
}
render () {
@ -99,7 +84,7 @@ class PreviewPresentation extends React.Component {
<ShareBanner>
<FlexRow className="preview-row">
<span className="share-text">
This project is not shared so only you can see it. Click share to let everyone see it!
This project is not shared so only you can see it. Click share to let everyone see it!
</span>
<Button className="button share-button">
Share
@ -130,8 +115,7 @@ class PreviewPresentation extends React.Component {
// })
}}
validations={{
// TODO: actual 100
maxLength: 40
maxLength: 100
}}
value={projectInfo.title}
/> :
@ -337,114 +321,16 @@ class PreviewPresentation extends React.Component {
Report
</Button>,
<ReportModal
contentLabel="Report Project"
isOpen={this.state.reportOpen}
key="report-modal"
type="project"
onReport={this.handleReportSubmit}
onRequestClose={this.handleReportClose}
onSubmit={this.handleReportSubmit}
>
From the dropdown below, please select the reason why you feel this project is disrespectful or inappropriate or otherwise breaks the Scratch Community Guidelines.
<Form
className="report"
onSubmit={this.handleReportSubmit}
>
<Input
name="projectId"
type="hidden"
value={projectId}
/>
<Input
name="username"
type="hidden"
value={user.username}
/>
<Select
required
name="reason"
options={[
{
value: '',
label: 'Select a reason'
},
{
value: '0',
label: 'Exact Copy of Project'
},
{
value: '1',
label: 'Uses Image/Music Without Credit'
},
{
value: '2',
label: 'Too Violent or Scary'
},
{
value: '3',
label: 'Inappropriate Language'
},
{
value: '4',
label: 'Inappropriate Music'
},
{
value: '8',
label: 'Inappropriate Images'
},
{
value: '5',
label: 'Sharing Personal Contact Information'
},
{
value: '6',
label: 'Other'
}
]}
value={this.state.reportReason}
onChange={this.handleReportReasonSelect}
/>
<TextArea
required
className="report-text"
name="reportText"
placeholder={this.state.reportPrompt}
validationErrors={{
maxLength: 'That\'s too long! Please find a way to shorten your text.',
minLength: 'That\'s too short. Please describe in detail what\'s inappropriate or disrespectful about the project.'
}}
validations={{
// TODO find out max and min characters
maxLength: 500,
minLength: 30
}}
/>
{this.state.reportWaiting ? [
<Button
className="submit-button white"
disabled="disabled"
key="submitButton"
type="submit"
>
<Spinner />
</Button>
] : [
<Button
className="submit-button white"
key="submitButton"
type="submit"
>
Send
</Button>
]}
</Form>
</ReportModal>
/>
]
}
</FlexRow>
</FlexRow>
</FlexRow>
<FlexRow className="preview-row">
<div className="comments-container">