Unverified Commit bf9622d9 authored by Sarah Otts's avatar Sarah Otts Committed by GitHub

Merge pull request #5577 from seotts/muted-errors-from-backend

Show errors when studio edit gets a muted response from backend
parents dfbce650 a7dae056
...@@ -21,7 +21,8 @@ const Errors = keyMirror({ ...@@ -21,7 +21,8 @@ const Errors = keyMirror({
THUMBNAIL_INVALID: null, THUMBNAIL_INVALID: null,
TEXT_TOO_LONG: null, TEXT_TOO_LONG: null,
REQUIRED_FIELD: null, REQUIRED_FIELD: null,
UNHANDLED: null UNHANDLED: null,
USER_MUTED: null
}); });
const MAX_IMAGE_BYTES = 524288; const MAX_IMAGE_BYTES = 524288;
...@@ -89,9 +90,7 @@ const selectFollowingMutationError = state => state.studioMutations.mutationErro ...@@ -89,9 +90,7 @@ const selectFollowingMutationError = state => state.studioMutations.mutationErro
const selectIsMutatingImage = state => state.studioMutations.isMutating.image; const selectIsMutatingImage = state => state.studioMutations.isMutating.image;
const selectImageMutationError = state => state.studioMutations.mutationErrors.image; const selectImageMutationError = state => state.studioMutations.mutationErrors.image;
const selectIsMutatingOpenToAll = state => state.studioMutations.isMutating.openToAll; const selectIsMutatingOpenToAll = state => state.studioMutations.isMutating.openToAll;
const selectOpenToAllMutationError = state => state.studioMutations.mutationErrors.openToAll;
const selectIsMutatingCommentsAllowed = state => state.studioMutations.isMutating.commentsAllowed; const selectIsMutatingCommentsAllowed = state => state.studioMutations.isMutating.commentsAllowed;
const selectCommentsAllowedMutationError = state => state.studioMutations.mutationErrors.commentsAllowed;
// Thunks // Thunks
/** /**
...@@ -104,6 +103,7 @@ const selectCommentsAllowedMutationError = state => state.studioMutations.mutati ...@@ -104,6 +103,7 @@ const selectCommentsAllowedMutationError = state => state.studioMutations.mutati
*/ */
const normalizeError = (err, body, res) => { const normalizeError = (err, body, res) => {
if (err) return Errors.NETWORK; if (err) return Errors.NETWORK;
if (res.statusCode === 403 && body.mute_status) return Errors.USER_MUTED;
if (res.statusCode === 401 || res.statusCode === 403) return Errors.PERMISSION; if (res.statusCode === 401 || res.statusCode === 403) return Errors.PERMISSION;
if (res.statusCode !== 200) return Errors.SERVER; if (res.statusCode !== 200) return Errors.SERVER;
try { try {
...@@ -221,6 +221,8 @@ const mutateStudioCommentsAllowed = shouldAllow => ((dispatch, getState) => { ...@@ -221,6 +221,8 @@ const mutateStudioCommentsAllowed = shouldAllow => ((dispatch, getState) => {
}, (err, body, res) => { }, (err, body, res) => {
const error = normalizeError(err, body, res); const error = normalizeError(err, body, res);
const wasAllowed = selectStudioCommentsAllowed(state); const wasAllowed = selectStudioCommentsAllowed(state);
// eslint-disable-next-line no-console
console.error(`Error mutating commentsAllowed: ${error}`);
dispatch(completeMutation('commentsAllowed', error ? wasAllowed : shouldAllow, error)); dispatch(completeMutation('commentsAllowed', error ? wasAllowed : shouldAllow, error));
}); });
}); });
...@@ -237,6 +239,8 @@ const mutateStudioOpenToAll = shouldBeOpen => ((dispatch, getState) => { ...@@ -237,6 +239,8 @@ const mutateStudioOpenToAll = shouldBeOpen => ((dispatch, getState) => {
}, (err, body, res) => { }, (err, body, res) => {
const error = normalizeError(err, body, res); const error = normalizeError(err, body, res);
const wasOpen = selectStudioOpenToAll(getState()); const wasOpen = selectStudioOpenToAll(getState());
// eslint-disable-next-line no-console
console.error(`Error mutating openToAll: ${error}`);
dispatch(completeMutation('openToAll', error ? wasOpen : shouldBeOpen, error)); dispatch(completeMutation('openToAll', error ? wasOpen : shouldBeOpen, error));
}); });
}); });
...@@ -264,7 +268,5 @@ module.exports = { ...@@ -264,7 +268,5 @@ module.exports = {
selectIsMutatingImage, selectIsMutatingImage,
selectImageMutationError, selectImageMutationError,
selectIsMutatingCommentsAllowed, selectIsMutatingCommentsAllowed,
selectCommentsAllowedMutationError, selectIsMutatingOpenToAll
selectIsMutatingOpenToAll,
selectOpenToAllMutationError
}; };
...@@ -111,10 +111,11 @@ ...@@ -111,10 +111,11 @@
"studio.reportThanksForLettingUsKnow": "Thanks for letting us know!", "studio.reportThanksForLettingUsKnow": "Thanks for letting us know!",
"studio.reportYourFeedback": "Your feedback will help us make Scratch better.", "studio.reportYourFeedback": "Your feedback will help us make Scratch better.",
"studios.mutedCurators": "You will be able to invite curators and add managers again {inDuration}.", "studio.mutedCurators": "You will be able to invite curators and add managers again {inDuration}.",
"studios.mutedProjects": "You will be able to add and remove projects again {inDuration}.", "studio.mutedProjects": "You will be able to add and remove projects again {inDuration}.",
"studios.mutedEdit": "You will be able to edit studios again {inDuration}.", "studio.mutedEdit": "You will be able to edit studios again {inDuration}.",
"studios.mutedPaused": "Your account has been paused from using studios until then.", "studio.mutedPaused": "Your account has been paused from using studios until then.",
"studio.mutedError": "Your account has been paused from using studios. Refresh for more information.",
"studio.alertProjectAdded": "\"{title}\" added to studio", "studio.alertProjectAdded": "\"{title}\" added to studio",
"studio.alertProjectAlreadyAdded": "That project is already in this studio", "studio.alertProjectAlreadyAdded": "That project is already in this studio",
......
...@@ -10,6 +10,7 @@ const Errors = keyMirror({ ...@@ -10,6 +10,7 @@ const Errors = keyMirror({
SERVER: null, SERVER: null,
PERMISSION: null, PERMISSION: null,
DUPLICATE: null, DUPLICATE: null,
USER_MUTED: null,
UNKNOWN_USERNAME: null, UNKNOWN_USERNAME: null,
RATE_LIMIT: null, RATE_LIMIT: null,
MANAGER_LIMIT: null MANAGER_LIMIT: null
...@@ -20,6 +21,7 @@ const normalizeError = (err, body, res) => { ...@@ -20,6 +21,7 @@ const normalizeError = (err, body, res) => {
if (res.statusCode === 400 && body.message === 'too many owners') { if (res.statusCode === 400 && body.message === 'too many owners') {
return Errors.MANAGER_LIMIT; return Errors.MANAGER_LIMIT;
} }
if (res.statusCode === 403 && body.mute_status) return Errors.USER_MUTED;
if (res.statusCode === 401 || res.statusCode === 403) return Errors.PERMISSION; if (res.statusCode === 401 || res.statusCode === 403) return Errors.PERMISSION;
if (res.statusCode === 404) return Errors.UNKNOWN_USERNAME; if (res.statusCode === 404) return Errors.UNKNOWN_USERNAME;
if (res.statusCode === 429) return Errors.RATE_LIMIT; if (res.statusCode === 429) return Errors.RATE_LIMIT;
......
...@@ -12,11 +12,13 @@ const Errors = keyMirror({ ...@@ -12,11 +12,13 @@ const Errors = keyMirror({
PERMISSION: null, PERMISSION: null,
UNKNOWN_PROJECT: null, UNKNOWN_PROJECT: null,
RATE_LIMIT: null, RATE_LIMIT: null,
DUPLICATE: null DUPLICATE: null,
USER_MUTED: null
}); });
const normalizeError = (err, body, res) => { const normalizeError = (err, body, res) => {
if (err) return Errors.NETWORK; if (err) return Errors.NETWORK;
if (res.statusCode === 403 && body.mute_status) return Errors.USER_MUTED;
if (res.statusCode === 401 || res.statusCode === 403) return Errors.PERMISSION; if (res.statusCode === 401 || res.statusCode === 403) return Errors.PERMISSION;
if (res.statusCode === 404) return Errors.UNKNOWN_PROJECT; if (res.statusCode === 404) return Errors.UNKNOWN_PROJECT;
if (res.statusCode === 409) return Errors.DUPLICATE; if (res.statusCode === 409) return Errors.DUPLICATE;
......
...@@ -7,13 +7,13 @@ import classNames from 'classnames'; ...@@ -7,13 +7,13 @@ import classNames from 'classnames';
import {selectStudioCommentsAllowed, selectIsFetchingInfo} from '../../redux/studio'; import {selectStudioCommentsAllowed, selectIsFetchingInfo} from '../../redux/studio';
import { import {
mutateStudioCommentsAllowed, selectIsMutatingCommentsAllowed, selectCommentsAllowedMutationError mutateStudioCommentsAllowed, selectIsMutatingCommentsAllowed
} from '../../redux/studio-mutations'; } from '../../redux/studio-mutations';
import ToggleSlider from '../../components/forms/toggle-slider.jsx'; import ToggleSlider from '../../components/forms/toggle-slider.jsx';
const StudioCommentsAllowed = ({ const StudioCommentsAllowed = ({
commentsAllowedError, isFetching, isMutating, commentsAllowed, handleUpdate isFetching, isMutating, commentsAllowed, handleUpdate
}) => ( }) => (
<div> <div>
{isFetching ? ( {isFetching ? (
...@@ -33,14 +33,12 @@ const StudioCommentsAllowed = ({ ...@@ -33,14 +33,12 @@ const StudioCommentsAllowed = ({
})} })}
onChange={e => handleUpdate(e.target.checked)} onChange={e => handleUpdate(e.target.checked)}
/> />
{commentsAllowedError && <div>Error mutating commentsAllowed: {commentsAllowedError}</div>}
</div> </div>
)} )}
</div> </div>
); );
StudioCommentsAllowed.propTypes = { StudioCommentsAllowed.propTypes = {
commentsAllowedError: PropTypes.string,
isFetching: PropTypes.bool, isFetching: PropTypes.bool,
isMutating: PropTypes.bool, isMutating: PropTypes.bool,
commentsAllowed: PropTypes.bool, commentsAllowed: PropTypes.bool,
...@@ -51,8 +49,7 @@ export default connect( ...@@ -51,8 +49,7 @@ export default connect(
state => ({ state => ({
commentsAllowed: selectStudioCommentsAllowed(state), commentsAllowed: selectStudioCommentsAllowed(state),
isFetching: selectIsFetchingInfo(state), isFetching: selectIsFetchingInfo(state),
isMutating: selectIsMutatingCommentsAllowed(state), isMutating: selectIsMutatingCommentsAllowed(state)
commentsAllowedError: selectCommentsAllowedMutationError(state)
}), }),
{ {
handleUpdate: mutateStudioCommentsAllowed handleUpdate: mutateStudioCommentsAllowed
......
...@@ -16,6 +16,7 @@ const errorToMessageId = error => { ...@@ -16,6 +16,7 @@ const errorToMessageId = error => {
case Errors.PERMISSION: return 'studio.curatorErrors.generic'; case Errors.PERMISSION: return 'studio.curatorErrors.generic';
case Errors.DUPLICATE: return 'studio.curatorErrors.alreadyCurator'; case Errors.DUPLICATE: return 'studio.curatorErrors.alreadyCurator';
case Errors.UNKNOWN_USERNAME: return 'studio.curatorErrors.unknownUsername'; case Errors.UNKNOWN_USERNAME: return 'studio.curatorErrors.unknownUsername';
case Errors.USER_MUTED: return 'studio.mutedError';
case Errors.RATE_LIMIT: return 'studio.curatorErrors.tooFast'; case Errors.RATE_LIMIT: return 'studio.curatorErrors.tooFast';
default: return 'studio.curatorErrors.generic'; default: return 'studio.curatorErrors.generic';
} }
......
...@@ -21,6 +21,7 @@ const errorToMessageId = error => { ...@@ -21,6 +21,7 @@ const errorToMessageId = error => {
case Errors.INAPPROPRIATE: return 'studio.updateErrors.inappropriate'; case Errors.INAPPROPRIATE: return 'studio.updateErrors.inappropriate';
case Errors.TEXT_TOO_LONG: return 'studio.updateErrors.textTooLong'; case Errors.TEXT_TOO_LONG: return 'studio.updateErrors.textTooLong';
case Errors.REQUIRED_FIELD: return 'studio.updateErrors.requiredField'; case Errors.REQUIRED_FIELD: return 'studio.updateErrors.requiredField';
case Errors.USER_MUTED: return 'studio.mutedError';
default: return 'studio.updateErrors.generic'; default: return 'studio.updateErrors.generic';
} }
}; };
......
...@@ -21,6 +21,7 @@ const errorToMessageId = error => { ...@@ -21,6 +21,7 @@ const errorToMessageId = error => {
switch (error) { switch (error) {
case Errors.THUMBNAIL_INVALID: return 'studio.updateErrors.thumbnailInvalid'; case Errors.THUMBNAIL_INVALID: return 'studio.updateErrors.thumbnailInvalid';
case Errors.THUMBNAIL_TOO_LARGE: return 'studio.updateErrors.thumbnailTooLarge'; case Errors.THUMBNAIL_TOO_LARGE: return 'studio.updateErrors.thumbnailTooLarge';
case Errors.USER_MUTED: return 'studio.mutedError';
default: return 'studio.updateErrors.generic'; default: return 'studio.updateErrors.generic';
} }
}; };
......
...@@ -14,7 +14,7 @@ const StudioMuteEditMessage = ({ ...@@ -14,7 +14,7 @@ const StudioMuteEditMessage = ({
<ValidationMessage <ValidationMessage
mode="info" mode="info"
message={<FormattedMessage message={<FormattedMessage
id="studios.mutedEdit" id="studio.mutedEdit"
values={{ values={{
inDuration: formatRelativeTime(muteExpiresAtMs, window._locale) inDuration: formatRelativeTime(muteExpiresAtMs, window._locale)
}} }}
......
...@@ -7,13 +7,13 @@ import classNames from 'classnames'; ...@@ -7,13 +7,13 @@ import classNames from 'classnames';
import {selectStudioOpenToAll, selectIsFetchingInfo} from '../../redux/studio'; import {selectStudioOpenToAll, selectIsFetchingInfo} from '../../redux/studio';
import { import {
mutateStudioOpenToAll, selectIsMutatingOpenToAll, selectOpenToAllMutationError mutateStudioOpenToAll, selectIsMutatingOpenToAll
} from '../../redux/studio-mutations'; } from '../../redux/studio-mutations';
import ToggleSlider from '../../components/forms/toggle-slider.jsx'; import ToggleSlider from '../../components/forms/toggle-slider.jsx';
const StudioOpenToAll = ({ const StudioOpenToAll = ({
openToAllError, isFetching, isMutating, openToAll, handleUpdate isFetching, isMutating, openToAll, handleUpdate
}) => ( }) => (
<div> <div>
{isFetching ? ( {isFetching ? (
...@@ -29,14 +29,12 @@ const StudioOpenToAll = ({ ...@@ -29,14 +29,12 @@ const StudioOpenToAll = ({
})} })}
onChange={e => handleUpdate(e.target.checked)} onChange={e => handleUpdate(e.target.checked)}
/> />
{openToAllError && <div>Error mutating openToAll: {openToAllError}</div>}
</div> </div>
)} )}
</div> </div>
); );
StudioOpenToAll.propTypes = { StudioOpenToAll.propTypes = {
openToAllError: PropTypes.string,
isFetching: PropTypes.bool, isFetching: PropTypes.bool,
isMutating: PropTypes.bool, isMutating: PropTypes.bool,
openToAll: PropTypes.bool, openToAll: PropTypes.bool,
...@@ -47,8 +45,7 @@ export default connect( ...@@ -47,8 +45,7 @@ export default connect(
state => ({ state => ({
openToAll: selectStudioOpenToAll(state), openToAll: selectStudioOpenToAll(state),
isFetching: selectIsFetchingInfo(state), isFetching: selectIsFetchingInfo(state),
isMutating: selectIsMutatingOpenToAll(state), isMutating: selectIsMutatingOpenToAll(state)
openToAllError: selectOpenToAllMutationError(state)
}), }),
{ {
handleUpdate: mutateStudioOpenToAll handleUpdate: mutateStudioOpenToAll
......
...@@ -18,6 +18,7 @@ const errorToMessageId = error => { ...@@ -18,6 +18,7 @@ const errorToMessageId = error => {
case Errors.DUPLICATE: return 'studio.projectErrors.duplicate'; case Errors.DUPLICATE: return 'studio.projectErrors.duplicate';
case Errors.RATE_LIMIT: return 'studio.projectErrors.tooFast'; case Errors.RATE_LIMIT: return 'studio.projectErrors.tooFast';
case Errors.UNKNOWN_PROJECT: return 'studio.projectErrors.checkUrl'; case Errors.UNKNOWN_PROJECT: return 'studio.projectErrors.checkUrl';
case Errors.USER_MUTED: return 'studio.mutedError';
default: return 'studio.projectErrors.generic'; default: return 'studio.projectErrors.generic';
} }
}; };
......
...@@ -38,13 +38,13 @@ const StudioProjects = ({ ...@@ -38,13 +38,13 @@ const StudioProjects = ({
<p> <p>
<div> <div>
<FormattedMessage <FormattedMessage
id="studios.mutedProjects" id="studio.mutedProjects"
values={{ values={{
inDuration: formatRelativeTime(muteExpiresAtMs, window._locale) inDuration: formatRelativeTime(muteExpiresAtMs, window._locale)
}} }}
/> />
</div> </div>
<div><FormattedMessage id="studios.mutedPaused" /></div> <div><FormattedMessage id="studio.mutedPaused" /></div>
</p> </p>
</CommentingStatus> </CommentingStatus>
} }
......
...@@ -17,6 +17,7 @@ const errorToMessageId = error => { ...@@ -17,6 +17,7 @@ const errorToMessageId = error => {
case Errors.INAPPROPRIATE: return 'studio.updateErrors.inappropriate'; case Errors.INAPPROPRIATE: return 'studio.updateErrors.inappropriate';
case Errors.TEXT_TOO_LONG: return 'studio.updateErrors.textTooLong'; case Errors.TEXT_TOO_LONG: return 'studio.updateErrors.textTooLong';
case Errors.REQUIRED_FIELD: return 'studio.updateErrors.requiredField'; case Errors.REQUIRED_FIELD: return 'studio.updateErrors.requiredField';
case Errors.USER_MUTED: return 'studio.mutedError';
default: return 'studio.updateErrors.generic'; default: return 'studio.updateErrors.generic';
} }
}; };
......
...@@ -48,7 +48,6 @@ import {selectShowCuratorMuteError} from '../../redux/studio-permissions.js'; ...@@ -48,7 +48,6 @@ import {selectShowCuratorMuteError} from '../../redux/studio-permissions.js';
const StudioShell = ({showCuratorMuteError, muteExpiresAtMs, studioLoadFailed}) => { const StudioShell = ({showCuratorMuteError, muteExpiresAtMs, studioLoadFailed}) => {
const match = useRouteMatch(); const match = useRouteMatch();
return ( return (
studioLoadFailed ? studioLoadFailed ?
<NotAvailable /> : <NotAvailable /> :
...@@ -68,13 +67,13 @@ const StudioShell = ({showCuratorMuteError, muteExpiresAtMs, studioLoadFailed}) ...@@ -68,13 +67,13 @@ const StudioShell = ({showCuratorMuteError, muteExpiresAtMs, studioLoadFailed})
<p> <p>
<div> <div>
<FormattedMessage <FormattedMessage
id="studios.mutedCurators" id="studio.mutedCurators"
values={{ values={{
inDuration: formatRelativeTime(muteExpiresAtMs, window._locale) inDuration: formatRelativeTime(muteExpiresAtMs, window._locale)
}} }}
/> />
</div> </div>
<div><FormattedMessage id="studios.mutedPaused" /></div> <div><FormattedMessage id="studio.mutedPaused" /></div>
</p> </p>
</CommentingStatus> </CommentingStatus>
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment