mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2024-11-23 23:57:55 -05:00
Merge pull request #5568 from ericrosenbaum/hide-validation
Hide validation message on click outside
This commit is contained in:
commit
b7eb303217
4 changed files with 55 additions and 13 deletions
|
@ -173,13 +173,14 @@ const mutateFollowingStudio = shouldFollow => ((dispatch, getState) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const mutateStudioImage = input => ((dispatch, getState) => {
|
const mutateStudioImage = input => ((dispatch, getState) => {
|
||||||
if (!input.files || !input.files[0]) return;
|
if (!input.files || !input.files[0]) return Promise.reject(new Error('no file'));
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const studioId = selectStudioId(state);
|
const studioId = selectStudioId(state);
|
||||||
const currentImage = selectStudioImage(state);
|
const currentImage = selectStudioImage(state);
|
||||||
dispatch(startMutation('image'));
|
dispatch(startMutation('image'));
|
||||||
if (input.files[0].size && input.files[0].size > MAX_IMAGE_BYTES) {
|
if (input.files[0].size && input.files[0].size > MAX_IMAGE_BYTES) {
|
||||||
return dispatch(completeMutation('image', currentImage, Errors.THUMBNAIL_TOO_LARGE));
|
dispatch(completeMutation('image', currentImage, Errors.THUMBNAIL_TOO_LARGE));
|
||||||
|
return Promise.reject(new Error('thumbnail too large'));
|
||||||
}
|
}
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('file', input.files[0]);
|
formData.append('file', input.files[0]);
|
||||||
|
|
|
@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
|
||||||
import {connect} from 'react-redux';
|
import {connect} from 'react-redux';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import {FormattedMessage} from 'react-intl';
|
import {FormattedMessage} from 'react-intl';
|
||||||
|
import onClickOutside from 'react-onclickoutside';
|
||||||
|
|
||||||
import {selectStudioDescription, selectIsFetchingInfo} from '../../redux/studio';
|
import {selectStudioDescription, selectIsFetchingInfo} from '../../redux/studio';
|
||||||
import {selectCanEditInfo, selectShowEditMuteError} from '../../redux/studio-permissions';
|
import {selectCanEditInfo, selectShowEditMuteError} from '../../redux/studio-permissions';
|
||||||
|
@ -28,6 +29,11 @@ const StudioDescription = ({
|
||||||
descriptionError, isFetching, isMutating, isMutedEditor, description, canEditInfo, handleUpdate
|
descriptionError, isFetching, isMutating, isMutedEditor, description, canEditInfo, handleUpdate
|
||||||
}) => {
|
}) => {
|
||||||
const [showMuteMessage, setShowMuteMessage] = useState(false);
|
const [showMuteMessage, setShowMuteMessage] = useState(false);
|
||||||
|
const [hideValidationMessage, setHideValidationMessage] = useState(false);
|
||||||
|
|
||||||
|
StudioDescription.handleClickOutside = () => {
|
||||||
|
setHideValidationMessage(true);
|
||||||
|
};
|
||||||
|
|
||||||
const fieldClassName = classNames('studio-description', {
|
const fieldClassName = classNames('studio-description', {
|
||||||
'mod-fetching': isFetching,
|
'mod-fetching': isFetching,
|
||||||
|
@ -49,10 +55,12 @@ const StudioDescription = ({
|
||||||
className={fieldClassName}
|
className={fieldClassName}
|
||||||
disabled={isMutating || isFetching || isMutedEditor}
|
disabled={isMutating || isFetching || isMutedEditor}
|
||||||
defaultValue={description}
|
defaultValue={description}
|
||||||
onBlur={e => e.target.value !== description &&
|
onBlur={e => {
|
||||||
handleUpdate(e.target.value)}
|
if (e.target.value !== description) handleUpdate(e.target.value);
|
||||||
|
setHideValidationMessage(false);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
{descriptionError && <ValidationMessage
|
{descriptionError && !hideValidationMessage && <ValidationMessage
|
||||||
mode="error"
|
mode="error"
|
||||||
message={<FormattedMessage id={errorToMessageId(descriptionError)} />}
|
message={<FormattedMessage id={errorToMessageId(descriptionError)} />}
|
||||||
/>}
|
/>}
|
||||||
|
@ -71,6 +79,10 @@ const StudioDescription = ({
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const clickOutsideConfig = {
|
||||||
|
handleClickOutside: () => StudioDescription.handleClickOutside
|
||||||
|
};
|
||||||
|
|
||||||
StudioDescription.propTypes = {
|
StudioDescription.propTypes = {
|
||||||
descriptionError: PropTypes.string,
|
descriptionError: PropTypes.string,
|
||||||
canEditInfo: PropTypes.bool,
|
canEditInfo: PropTypes.bool,
|
||||||
|
@ -81,7 +93,7 @@ StudioDescription.propTypes = {
|
||||||
handleUpdate: PropTypes.func
|
handleUpdate: PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(
|
const connectedStudioDescription = connect(
|
||||||
state => ({
|
state => ({
|
||||||
description: selectStudioDescription(state),
|
description: selectStudioDescription(state),
|
||||||
canEditInfo: selectCanEditInfo(state),
|
canEditInfo: selectCanEditInfo(state),
|
||||||
|
@ -94,3 +106,5 @@ export default connect(
|
||||||
handleUpdate: mutateStudioDescription
|
handleUpdate: mutateStudioDescription
|
||||||
}
|
}
|
||||||
)(StudioDescription);
|
)(StudioDescription);
|
||||||
|
|
||||||
|
export default onClickOutside(connectedStudioDescription, clickOutsideConfig);
|
||||||
|
|
|
@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
|
||||||
import {connect} from 'react-redux';
|
import {connect} from 'react-redux';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import {FormattedMessage} from 'react-intl';
|
import {FormattedMessage} from 'react-intl';
|
||||||
|
import onClickOutside from 'react-onclickoutside';
|
||||||
|
|
||||||
import {selectStudioImage, selectIsFetchingInfo} from '../../redux/studio';
|
import {selectStudioImage, selectIsFetchingInfo} from '../../redux/studio';
|
||||||
import {selectCanEditInfo, selectShowEditMuteError} from '../../redux/studio-permissions';
|
import {selectCanEditInfo, selectShowEditMuteError} from '../../redux/studio-permissions';
|
||||||
|
@ -14,7 +15,6 @@ import {
|
||||||
import ValidationMessage from '../../components/forms/validation-message.jsx';
|
import ValidationMessage from '../../components/forms/validation-message.jsx';
|
||||||
import StudioMuteEditMessage from './studio-mute-edit-message.jsx';
|
import StudioMuteEditMessage from './studio-mute-edit-message.jsx';
|
||||||
|
|
||||||
|
|
||||||
import editIcon from './icons/edit-icon.svg';
|
import editIcon from './icons/edit-icon.svg';
|
||||||
|
|
||||||
const errorToMessageId = error => {
|
const errorToMessageId = error => {
|
||||||
|
@ -43,6 +43,11 @@ const StudioImage = ({
|
||||||
});
|
});
|
||||||
|
|
||||||
const [showMuteMessage, setShowMuteMessage] = useState(false);
|
const [showMuteMessage, setShowMuteMessage] = useState(false);
|
||||||
|
const [hideValidationMessage, setHideValidationMessage] = useState(false);
|
||||||
|
|
||||||
|
StudioImage.handleClickOutside = () => {
|
||||||
|
setHideValidationMessage(true);
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={fieldClassName}
|
className={fieldClassName}
|
||||||
|
@ -76,11 +81,13 @@ const StudioImage = ({
|
||||||
accept="image/*"
|
accept="image/*"
|
||||||
onChange={e => {
|
onChange={e => {
|
||||||
handleUpdate(e.target)
|
handleUpdate(e.target)
|
||||||
|
.catch(() => { /* errors are handled in the reducer */ })
|
||||||
.then(dataUrl => setUploadPreview(dataUrl));
|
.then(dataUrl => setUploadPreview(dataUrl));
|
||||||
e.target.value = '';
|
e.target.value = '';
|
||||||
|
setHideValidationMessage(false);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{imageError && <ValidationMessage
|
{imageError && !hideValidationMessage && <ValidationMessage
|
||||||
mode="error"
|
mode="error"
|
||||||
message={<FormattedMessage id={errorToMessageId(imageError)} />}
|
message={<FormattedMessage id={errorToMessageId(imageError)} />}
|
||||||
/>}
|
/>}
|
||||||
|
@ -91,6 +98,10 @@ const StudioImage = ({
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const clickOutsideConfig = {
|
||||||
|
handleClickOutside: () => StudioImage.handleClickOutside
|
||||||
|
};
|
||||||
|
|
||||||
StudioImage.propTypes = {
|
StudioImage.propTypes = {
|
||||||
imageError: PropTypes.string,
|
imageError: PropTypes.string,
|
||||||
canEditInfo: PropTypes.bool,
|
canEditInfo: PropTypes.bool,
|
||||||
|
@ -101,7 +112,7 @@ StudioImage.propTypes = {
|
||||||
handleUpdate: PropTypes.func
|
handleUpdate: PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(
|
const connectedStudioImage = connect(
|
||||||
state => ({
|
state => ({
|
||||||
image: selectStudioImage(state),
|
image: selectStudioImage(state),
|
||||||
canEditInfo: selectCanEditInfo(state),
|
canEditInfo: selectCanEditInfo(state),
|
||||||
|
@ -114,3 +125,5 @@ export default connect(
|
||||||
handleUpdate: mutateStudioImage
|
handleUpdate: mutateStudioImage
|
||||||
}
|
}
|
||||||
)(StudioImage);
|
)(StudioImage);
|
||||||
|
|
||||||
|
export default onClickOutside(connectedStudioImage, clickOutsideConfig);
|
||||||
|
|
|
@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
|
||||||
import {connect} from 'react-redux';
|
import {connect} from 'react-redux';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import {FormattedMessage} from 'react-intl';
|
import {FormattedMessage} from 'react-intl';
|
||||||
|
import onClickOutside from 'react-onclickoutside';
|
||||||
|
|
||||||
import {selectStudioTitle, selectIsFetchingInfo} from '../../redux/studio';
|
import {selectStudioTitle, selectIsFetchingInfo} from '../../redux/studio';
|
||||||
import {selectCanEditInfo, selectShowEditMuteError} from '../../redux/studio-permissions';
|
import {selectCanEditInfo, selectShowEditMuteError} from '../../redux/studio-permissions';
|
||||||
|
@ -31,6 +32,11 @@ const StudioTitle = ({
|
||||||
});
|
});
|
||||||
|
|
||||||
const [showMuteMessage, setShowMuteMessage] = useState(false);
|
const [showMuteMessage, setShowMuteMessage] = useState(false);
|
||||||
|
const [hideValidationMessage, setHideValidationMessage] = useState(false);
|
||||||
|
|
||||||
|
StudioTitle.handleClickOutside = () => {
|
||||||
|
setHideValidationMessage(true);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
@ -45,10 +51,12 @@ const StudioTitle = ({
|
||||||
disabled={isMutating || !canEditInfo || isFetching}
|
disabled={isMutating || !canEditInfo || isFetching}
|
||||||
defaultValue={title}
|
defaultValue={title}
|
||||||
onKeyDown={e => e.key === 'Enter' && e.target.blur()}
|
onKeyDown={e => e.key === 'Enter' && e.target.blur()}
|
||||||
onBlur={e => e.target.value !== title &&
|
onBlur={e => {
|
||||||
handleUpdate(e.target.value)}
|
if (e.target.value !== title) handleUpdate(e.target.value);
|
||||||
|
setHideValidationMessage(false);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
{titleError && <ValidationMessage
|
{titleError && !hideValidationMessage && <ValidationMessage
|
||||||
mode="error"
|
mode="error"
|
||||||
message={<FormattedMessage id={errorToMessageId(titleError)} />}
|
message={<FormattedMessage id={errorToMessageId(titleError)} />}
|
||||||
/>}
|
/>}
|
||||||
|
@ -61,6 +69,10 @@ const StudioTitle = ({
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const clickOutsideConfig = {
|
||||||
|
handleClickOutside: () => StudioTitle.handleClickOutside
|
||||||
|
};
|
||||||
|
|
||||||
StudioTitle.propTypes = {
|
StudioTitle.propTypes = {
|
||||||
titleError: PropTypes.string,
|
titleError: PropTypes.string,
|
||||||
canEditInfo: PropTypes.bool,
|
canEditInfo: PropTypes.bool,
|
||||||
|
@ -71,7 +83,7 @@ StudioTitle.propTypes = {
|
||||||
handleUpdate: PropTypes.func
|
handleUpdate: PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(
|
const connectedStudioTitle = connect(
|
||||||
state => ({
|
state => ({
|
||||||
title: selectStudioTitle(state),
|
title: selectStudioTitle(state),
|
||||||
canEditInfo: selectCanEditInfo(state),
|
canEditInfo: selectCanEditInfo(state),
|
||||||
|
@ -84,3 +96,5 @@ export default connect(
|
||||||
handleUpdate: mutateStudioTitle
|
handleUpdate: mutateStudioTitle
|
||||||
}
|
}
|
||||||
)(StudioTitle);
|
)(StudioTitle);
|
||||||
|
|
||||||
|
export default onClickOutside(connectedStudioTitle, clickOutsideConfig);
|
||||||
|
|
Loading…
Reference in a new issue