mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2024-11-23 07:38:07 -05:00
Merge pull request #2714 from LLK/hotfix/show-update-errors-hoc
[Master] Wrap project inputs to show server validation
This commit is contained in:
commit
db99c12d89
5 changed files with 178 additions and 80 deletions
79
src/views/preview/formsy-project-updater.jsx
Normal file
79
src/views/preview/formsy-project-updater.jsx
Normal file
|
@ -0,0 +1,79 @@
|
|||
const PropTypes = require('prop-types');
|
||||
const React = require('react');
|
||||
const bindAll = require('lodash.bindall');
|
||||
const api = require('../../lib/api');
|
||||
const connect = require('react-redux').connect;
|
||||
const injectIntl = require('react-intl').injectIntl;
|
||||
const intlShape = require('react-intl').intlShape;
|
||||
|
||||
class FormsyProjectUpdater extends React.Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
bindAll(this, [
|
||||
'setRef',
|
||||
'handleUpdate'
|
||||
]);
|
||||
this.state = {
|
||||
value: props.initialValue,
|
||||
error: false
|
||||
};
|
||||
}
|
||||
componentDidUpdate () {
|
||||
if (this.state.error !== false) {
|
||||
const errorMessageId = this.state.error === 400 ?
|
||||
'project.inappropriateUpdate' : 'general.notAvailableHeadline';
|
||||
this.ref.updateInputsWithError({
|
||||
[this.props.field]: this.props.intl.formatMessage({
|
||||
id: errorMessageId
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
handleUpdate (jsonData) {
|
||||
// Ignore updates that would not change the value
|
||||
if (jsonData[this.props.field] === this.state.value) return;
|
||||
|
||||
api({
|
||||
uri: `/projects/${this.props.projectInfo.id}`,
|
||||
authentication: this.props.user.token,
|
||||
method: 'PUT',
|
||||
json: jsonData
|
||||
}, (err, body, res) => {
|
||||
if (res.statusCode === 200) {
|
||||
this.setState({value: body[this.props.field], error: false});
|
||||
} else {
|
||||
this.setState({error: res.statusCode});
|
||||
}
|
||||
});
|
||||
}
|
||||
setRef (ref) {
|
||||
this.ref = ref;
|
||||
}
|
||||
render () {
|
||||
return this.props.children(
|
||||
this.state.value,
|
||||
this.setRef,
|
||||
this.handleUpdate
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
FormsyProjectUpdater.propTypes = {
|
||||
children: PropTypes.func.isRequired,
|
||||
field: PropTypes.string,
|
||||
initialValue: PropTypes.string,
|
||||
intl: intlShape,
|
||||
projectInfo: PropTypes.shape({
|
||||
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
|
||||
}),
|
||||
user: PropTypes.shape({
|
||||
token: PropTypes.string
|
||||
})
|
||||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
projectInfo: state.preview.projectInfo,
|
||||
user: state.session.session.user
|
||||
});
|
||||
|
||||
module.exports = connect(mapStateToProps)(injectIntl(FormsyProjectUpdater));
|
|
@ -43,5 +43,6 @@
|
|||
"project.cloudDataAlert": "This project uses cloud data - a feature that is only available to signed in Scratchers.",
|
||||
"project.cloudVariables": "Cloud Variables",
|
||||
"project.cloudDataLink": "See Data",
|
||||
"project.usernameBlockAlert": "This project can detect who is using it, through the \"username\" block. To hide your identity, sign out before using the project."
|
||||
"project.usernameBlockAlert": "This project can detect who is using it, through the \"username\" block. To hide your identity, sign out before using the project.",
|
||||
"project.inappropriateUpdate": "Hmm...the bad word detector thinks there is a problem with your text. Please change it and remember to be respectful."
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ const TopLevelComment = require('./comment/top-level-comment.jsx');
|
|||
const ComposeComment = require('./comment/compose-comment.jsx');
|
||||
const ExtensionChip = require('./extension-chip.jsx');
|
||||
const thumbnailUrl = require('../../lib/user-thumbnail');
|
||||
const FormsyProjectUpdater = require('./formsy-project-updater.jsx');
|
||||
|
||||
const projectShape = require('./projectshape.jsx').projectShape;
|
||||
require('./preview.scss');
|
||||
|
@ -107,7 +108,6 @@ const PreviewPresentation = ({
|
|||
onShare,
|
||||
onToggleComments,
|
||||
onToggleStudio,
|
||||
onUpdate,
|
||||
onUpdateProjectId,
|
||||
onUpdateProjectThumbnail,
|
||||
originalInfo,
|
||||
|
@ -233,22 +233,32 @@ const PreviewPresentation = ({
|
|||
</a>
|
||||
<div className="title">
|
||||
{editable ?
|
||||
<Formsy onKeyPress={onKeyPress}>
|
||||
<InplaceInput
|
||||
className="project-title"
|
||||
handleUpdate={onUpdate}
|
||||
name="title"
|
||||
validationErrors={{
|
||||
maxLength: intl.formatMessage({
|
||||
id: 'project.titleMaxLength'
|
||||
})
|
||||
}}
|
||||
validations={{
|
||||
maxLength: 100
|
||||
}}
|
||||
value={projectInfo.title}
|
||||
/>
|
||||
</Formsy> :
|
||||
<FormsyProjectUpdater
|
||||
field="title"
|
||||
initialValue={projectInfo.title}
|
||||
>
|
||||
{(value, ref, handleUpdate) => (
|
||||
<Formsy
|
||||
ref={ref}
|
||||
onKeyPress={onKeyPress}
|
||||
>
|
||||
<InplaceInput
|
||||
className="project-title"
|
||||
handleUpdate={handleUpdate}
|
||||
name="title"
|
||||
validationErrors={{
|
||||
maxLength: intl.formatMessage({
|
||||
id: 'project.titleMaxLength'
|
||||
})
|
||||
}}
|
||||
validations={{
|
||||
maxLength: 100
|
||||
}}
|
||||
value={value}
|
||||
/>
|
||||
</Formsy>
|
||||
)}
|
||||
</FormsyProjectUpdater> :
|
||||
<React.Fragment>
|
||||
<div
|
||||
className="project-title no-edit"
|
||||
|
@ -377,32 +387,40 @@ const PreviewPresentation = ({
|
|||
<FormattedMessage id="project.instructionsLabel" />
|
||||
</div>
|
||||
{editable ?
|
||||
<Formsy
|
||||
className="project-description-form"
|
||||
onKeyPress={onKeyPress}
|
||||
<FormsyProjectUpdater
|
||||
field="instructions"
|
||||
initialValue={projectInfo.instructions}
|
||||
>
|
||||
<InplaceInput
|
||||
className={classNames(
|
||||
'project-description-edit',
|
||||
{remixes: parentInfo && parentInfo.author}
|
||||
)}
|
||||
handleUpdate={onUpdate}
|
||||
name="instructions"
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'project.descriptionPlaceholder'
|
||||
})}
|
||||
type="textarea"
|
||||
validationErrors={{
|
||||
maxLength: intl.formatMessage({
|
||||
id: 'project.descriptionMaxLength'
|
||||
})
|
||||
}}
|
||||
validations={{
|
||||
maxLength: 5000
|
||||
}}
|
||||
value={projectInfo.instructions}
|
||||
/>
|
||||
</Formsy> :
|
||||
{(value, ref, handleUpdate) => (
|
||||
<Formsy
|
||||
className="project-description-form"
|
||||
ref={ref}
|
||||
onKeyPress={onKeyPress}
|
||||
>
|
||||
<InplaceInput
|
||||
className={classNames(
|
||||
'project-description-edit',
|
||||
{remixes: parentInfo && parentInfo.author}
|
||||
)}
|
||||
handleUpdate={handleUpdate}
|
||||
name="instructions"
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'project.descriptionPlaceholder'
|
||||
})}
|
||||
type="textarea"
|
||||
validationErrors={{
|
||||
maxLength: intl.formatMessage({
|
||||
id: 'project.descriptionMaxLength'
|
||||
})
|
||||
}}
|
||||
validations={{
|
||||
maxLength: 5000
|
||||
}}
|
||||
value={value}
|
||||
/>
|
||||
</Formsy>
|
||||
)}
|
||||
</FormsyProjectUpdater> :
|
||||
<div className="project-description">
|
||||
{decorateText(projectInfo.instructions, {
|
||||
usernames: true,
|
||||
|
@ -419,33 +437,41 @@ const PreviewPresentation = ({
|
|||
<FormattedMessage id="project.notesAndCreditsLabel" />
|
||||
</div>
|
||||
{editable ?
|
||||
<Formsy
|
||||
className="project-description-form"
|
||||
onKeyPress={onKeyPress}
|
||||
<FormsyProjectUpdater
|
||||
field="description"
|
||||
initialValue={projectInfo.description}
|
||||
>
|
||||
<InplaceInput
|
||||
className={classNames(
|
||||
'project-description-edit',
|
||||
'last',
|
||||
{remixes: parentInfo && parentInfo.author}
|
||||
)}
|
||||
handleUpdate={onUpdate}
|
||||
name="description"
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'project.notesPlaceholder'
|
||||
})}
|
||||
type="textarea"
|
||||
validationErrors={{
|
||||
maxLength: intl.formatMessage({
|
||||
id: 'project.descriptionMaxLength'
|
||||
})
|
||||
}}
|
||||
validations={{
|
||||
maxLength: 5000
|
||||
}}
|
||||
value={projectInfo.description}
|
||||
/>
|
||||
</Formsy> :
|
||||
{(value, ref, handleUpdate) => (
|
||||
<Formsy
|
||||
className="project-description-form"
|
||||
ref={ref}
|
||||
onKeyPress={onKeyPress}
|
||||
>
|
||||
<InplaceInput
|
||||
className={classNames(
|
||||
'project-description-edit',
|
||||
'last',
|
||||
{remixes: parentInfo && parentInfo.author}
|
||||
)}
|
||||
handleUpdate={handleUpdate}
|
||||
name="description"
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'project.notesPlaceholder'
|
||||
})}
|
||||
type="textarea"
|
||||
validationErrors={{
|
||||
maxLength: intl.formatMessage({
|
||||
id: 'project.descriptionMaxLength'
|
||||
})
|
||||
}}
|
||||
validations={{
|
||||
maxLength: 5000
|
||||
}}
|
||||
value={value}
|
||||
/>
|
||||
</Formsy>
|
||||
)}
|
||||
</FormsyProjectUpdater> :
|
||||
<div className="project-description last">
|
||||
{decorateText(projectInfo.description, {
|
||||
usernames: true,
|
||||
|
@ -675,7 +701,6 @@ PreviewPresentation.propTypes = {
|
|||
onShare: PropTypes.func,
|
||||
onToggleComments: PropTypes.func,
|
||||
onToggleStudio: PropTypes.func,
|
||||
onUpdate: PropTypes.func,
|
||||
onUpdateProjectId: PropTypes.func,
|
||||
onUpdateProjectThumbnail: PropTypes.func,
|
||||
originalInfo: projectShape,
|
||||
|
|
|
@ -149,7 +149,7 @@ $stage-width: 480px;
|
|||
$arrow-border-width: 1rem;
|
||||
display: block;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
z-index: 5;
|
||||
margin-top: $arrow-border-width;
|
||||
border: 1px solid $active-gray;
|
||||
border-radius: 5px;
|
||||
|
|
|
@ -82,7 +82,6 @@ class Preview extends React.Component {
|
|||
'handleShare',
|
||||
'handleUpdateProjectId',
|
||||
'handleUpdateProjectTitle',
|
||||
'handleUpdate',
|
||||
'handleToggleComments',
|
||||
'initCounts',
|
||||
'pushHistory',
|
||||
|
@ -523,19 +522,14 @@ class Preview extends React.Component {
|
|||
justShared: true
|
||||
});
|
||||
}
|
||||
handleUpdate (jsonData) {
|
||||
handleUpdateProjectTitle (title) {
|
||||
this.props.updateProject(
|
||||
this.props.projectInfo.id,
|
||||
jsonData,
|
||||
{title: title},
|
||||
this.props.user.username,
|
||||
this.props.user.token
|
||||
);
|
||||
}
|
||||
handleUpdateProjectTitle (title) {
|
||||
this.handleUpdate({
|
||||
title: title
|
||||
});
|
||||
}
|
||||
handleSetLanguage (locale) {
|
||||
jar.set('scratchlanguage', locale);
|
||||
}
|
||||
|
@ -696,7 +690,6 @@ class Preview extends React.Component {
|
|||
onShare={this.handleShare}
|
||||
onToggleComments={this.handleToggleComments}
|
||||
onToggleStudio={this.handleToggleStudio}
|
||||
onUpdate={this.handleUpdate}
|
||||
onUpdateProjectId={this.handleUpdateProjectId}
|
||||
onUpdateProjectThumbnail={this.props.handleUpdateProjectThumbnail}
|
||||
/>
|
||||
|
|
Loading…
Reference in a new issue