mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2024-11-27 09:35:56 -05:00
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:
parent
34042c9e8e
commit
08cf4f4f3d
3 changed files with 215 additions and 153 deletions
|
@ -1,34 +1,186 @@
|
||||||
|
const bindAll = require('lodash.bindall');
|
||||||
const PropTypes = require('prop-types');
|
const PropTypes = require('prop-types');
|
||||||
const React = require('react');
|
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 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('../../forms/button.scss');
|
||||||
require('./modal.scss');
|
require('./modal.scss');
|
||||||
|
|
||||||
const ReportModal = props => (
|
class ReportModal extends React.Component {
|
||||||
<Modal
|
constructor (props) {
|
||||||
className="mod-report"
|
super(props);
|
||||||
{...props}
|
bindAll(this, [
|
||||||
>
|
'handleReasonSelect',
|
||||||
<div>
|
'handleSubmit'
|
||||||
<div className="report-modal-header">
|
]);
|
||||||
<div className="report-content-label">
|
this.state = {
|
||||||
{props.contentLabel}
|
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">
|
||||||
|
<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>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="report-modal-content">
|
</Modal>
|
||||||
{props.children}
|
);
|
||||||
</div>
|
}
|
||||||
</div>
|
}
|
||||||
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
|
|
||||||
ReportModal.propTypes = {
|
ReportModal.propTypes = {
|
||||||
children: PropTypes.node,
|
intl: intlShape,
|
||||||
contentLabel: PropTypes.string,
|
onReport: PropTypes.func,
|
||||||
onRequestClose: PropTypes.func
|
onRequestClose: PropTypes.func,
|
||||||
|
type: PropTypes.string
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = ReportModal;
|
module.exports = injectIntl(ReportModal);
|
||||||
|
|
|
@ -159,5 +159,29 @@
|
||||||
"registration.welcomeStepPrompt": "To get started, click on the button below.",
|
"registration.welcomeStepPrompt": "To get started, click on the button below.",
|
||||||
"registration.welcomeStepTitle": "Hurray! Welcome to Scratch!",
|
"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"
|
||||||
}
|
}
|
|
@ -14,11 +14,6 @@ const sessionActions = require('../../redux/session.js');
|
||||||
const decorateText = require('../../lib/decorate-text.jsx');
|
const decorateText = require('../../lib/decorate-text.jsx');
|
||||||
const FlexRow = require('../../components/flex-row/flex-row.jsx');
|
const FlexRow = require('../../components/flex-row/flex-row.jsx');
|
||||||
const Button = require('../../components/forms/button.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 Avatar = require('../../components/avatar/avatar.jsx');
|
||||||
const CappedNumber = require('../../components/cappednumber/cappednumber.jsx');
|
const CappedNumber = require('../../components/cappednumber/cappednumber.jsx');
|
||||||
const ShareBanner = require('../../components/share-banner/share-banner.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 InplaceInput = require('../../components/forms/inplace-input.jsx');
|
||||||
const ReportModal = require('../../components/modal/report/modal.jsx');
|
const ReportModal = require('../../components/modal/report/modal.jsx');
|
||||||
|
|
||||||
|
|
||||||
require('./preview.scss');
|
require('./preview.scss');
|
||||||
|
|
||||||
class PreviewPresentation extends React.Component {
|
class PreviewPresentation extends React.Component {
|
||||||
|
@ -35,13 +29,10 @@ class PreviewPresentation extends React.Component {
|
||||||
bindAll(this, [
|
bindAll(this, [
|
||||||
'handleReportClick',
|
'handleReportClick',
|
||||||
'handleReportClose',
|
'handleReportClose',
|
||||||
'handleReportReasonSelect',
|
|
||||||
'handleReportSubmit'
|
'handleReportSubmit'
|
||||||
]);
|
]);
|
||||||
this.state = {
|
this.state = {
|
||||||
reportOpen: false,
|
reportOpen: false
|
||||||
reportPrompt: 'Select a reason why above.',
|
|
||||||
reportReason: ''
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
handleReportClick (e) {
|
handleReportClick (e) {
|
||||||
|
@ -51,22 +42,16 @@ class PreviewPresentation extends React.Component {
|
||||||
handleReportClose () {
|
handleReportClose () {
|
||||||
this.setState({reportOpen: false});
|
this.setState({reportOpen: false});
|
||||||
}
|
}
|
||||||
handleReportReasonSelect (name, value) {
|
|
||||||
const prompts = [
|
handleReportSubmit (formData, callback) {
|
||||||
'Please provide a link to the original project',
|
const data = {
|
||||||
'Please provide links to the uncredited content',
|
...formData,
|
||||||
'Please say why the project is too violent or scary',
|
id: this.props.projectId,
|
||||||
'Please say where the inappropriate language occurs in the project (For example: Notes & Credits, sprite name, project text, etc.)',
|
username: this.props.user.username
|
||||||
'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.)',
|
console.log('submit report data', data);
|
||||||
'Please be specific about why this project does not follow our Community Guidelines',
|
// TODO: pass error to modal via callback.
|
||||||
'not used',
|
callback();
|
||||||
'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);
|
|
||||||
this.setState({reportOpen: false});
|
this.setState({reportOpen: false});
|
||||||
}
|
}
|
||||||
render () {
|
render () {
|
||||||
|
@ -99,7 +84,7 @@ class PreviewPresentation extends React.Component {
|
||||||
<ShareBanner>
|
<ShareBanner>
|
||||||
<FlexRow className="preview-row">
|
<FlexRow className="preview-row">
|
||||||
<span className="share-text">
|
<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>
|
</span>
|
||||||
<Button className="button share-button">
|
<Button className="button share-button">
|
||||||
Share
|
Share
|
||||||
|
@ -130,8 +115,7 @@ class PreviewPresentation extends React.Component {
|
||||||
// })
|
// })
|
||||||
}}
|
}}
|
||||||
validations={{
|
validations={{
|
||||||
// TODO: actual 100
|
maxLength: 100
|
||||||
maxLength: 40
|
|
||||||
}}
|
}}
|
||||||
value={projectInfo.title}
|
value={projectInfo.title}
|
||||||
/> :
|
/> :
|
||||||
|
@ -337,114 +321,16 @@ class PreviewPresentation extends React.Component {
|
||||||
Report
|
Report
|
||||||
</Button>,
|
</Button>,
|
||||||
<ReportModal
|
<ReportModal
|
||||||
contentLabel="Report Project"
|
|
||||||
isOpen={this.state.reportOpen}
|
isOpen={this.state.reportOpen}
|
||||||
key="report-modal"
|
key="report-modal"
|
||||||
|
type="project"
|
||||||
|
onReport={this.handleReportSubmit}
|
||||||
onRequestClose={this.handleReportClose}
|
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>
|
||||||
|
|
||||||
</FlexRow>
|
</FlexRow>
|
||||||
<FlexRow className="preview-row">
|
<FlexRow className="preview-row">
|
||||||
<div className="comments-container">
|
<div className="comments-container">
|
||||||
|
|
Loading…
Reference in a new issue