diff --git a/src/redux/session.js b/src/redux/session.js index 6343f5aef..48c85acb3 100644 --- a/src/redux/session.js +++ b/src/redux/session.js @@ -16,6 +16,7 @@ const banGoodListPaths = [ '/ip_ban_appeal', '/vpn_required', '/accounts/banned-response', + '/accounts/bad-username', '/community_guidelines', '/privacy_policy', '/terms_of_use' @@ -85,7 +86,11 @@ const handleSessionResponse = (dispatch, body) => { body.user.banned && banGoodListPaths.every(goodPath => window.location.pathname.indexOf(goodPath) === -1) ) { - window.location = '/accounts/banned-response/'; + if (body.user.banned_status === 'far_banned'){ + window.location = '/accounts/bad-username/'; + } else { + window.location = '/accounts/banned-response/'; + } return; } else if ( body.flags && diff --git a/src/routes.json b/src/routes.json index 06e47904d..e7a033616 100644 --- a/src/routes.json +++ b/src/routes.json @@ -430,6 +430,13 @@ "view": "microbit/microbit", "title": "micro:bit" }, + { + "name": "bad-username-splash", + "pattern": "^/accounts/bad-username/?(\\?.*)?$", + "routeAlias": "/accounts/bad-username/?$", + "view": "bad-username-splash/bad-username-splash", + "title": "Account Blocked" + }, { "name": "vernier", "pattern": "^/vernier/?(\\?.*)?$", diff --git a/src/views/bad-username-splash/bad-username-splash.jsx b/src/views/bad-username-splash/bad-username-splash.jsx new file mode 100644 index 000000000..dbd317ee8 --- /dev/null +++ b/src/views/bad-username-splash/bad-username-splash.jsx @@ -0,0 +1,290 @@ +const injectIntl = require('react-intl').injectIntl; +const React = require('react'); +const FormattedMessage = require('react-intl').FormattedMessage; +import {connect} from 'react-redux'; +import {selectUser, selectHasFetchedSession} from '../../redux/session'; +const messageActions = require('../../redux/messages.js'); +const JoinFlowStep = require('../../components/join-flow/join-flow-step.jsx'); +const FormikInput = require('../../components/formik-forms/formik-input.jsx'); +import {Formik} from 'formik'; +const PropTypes = require('prop-types'); + +const Page = require('../../components/page/www/page.jsx'); +const render = require('../../lib/render.jsx'); +const api = require('../../lib/api'); +import bannedIcon from './blocked-account.svg'; + +require('../../components/extension-landing/extension-landing.scss'); +require('./bad-username-splash.scss'); + +const validateNewUsernameForm = values => { + const errors = {}; + if (values.canValidate && (values.newUsername !== values.newUsernameConfirm && values.newUsernameConfirm !== '')){ + errors.newUsernameConfirm = "usernames don't match"; + } + return errors; +}; + +const PIIUsernameMessage = 'username appears to contain personal information'; +const BadUsernameMessage = 'an inappropriate username'; + +const BannedSplash = ({hasSession, user, adminMessages, getAdminMessages}) => { + + const [unauthorizedError, setUnauthorizedError] = React.useState(false); + const [badUsernameError, setBadUsernameError] = React.useState(false); + const [apiError, setAPIError] = React.useState(false); + + const latestAdminMessage = adminMessages && adminMessages[0] && adminMessages[0].message; + + React.useEffect(() => { + if (user && user.username && user.token){ + getAdminMessages(user.username, user.token); + } + }, [user]); + + const handleUpdateUsernameUnbanSubmit = (formData, formikBag) => { + setUnauthorizedError(false); + setBadUsernameError(false); + setAPIError(false); + formikBag.setSubmitting(false); // formik makes us do this ourselves + + api({ + host: '', + uri: '/accounts/update_username/', + method: 'post', + useCsrf: true, + json: { + new_username: formData.newUsername, + username: formData.username, + password: formData.password + } + }, (err, body, res) => { + if (res.body.error === 'Unauthorized'){ + setUnauthorizedError('error message for unauthorized access'); + } else if (res.body.error === 'Invalid username'){ + setBadUsernameError('error message for invalid username'); + } else if (res.body.error){ + setAPIError('error message for API error'); + } else { + window.location = '/'; + } + }); + }; + + if (hasSession && (!user || !user.banned)){ + window.location = '/'; + } + + if (user && user.banned){ + return (
+
+
+
+ + +

+
+

+ + {latestAdminMessage && latestAdminMessage.includes(PIIUsernameMessage) && + (
+

+

+

+
) + } + {latestAdminMessage && latestAdminMessage.includes(BadUsernameMessage) && + (
+

+

+

+ + + ) + }} + />

+
) + } +
+ +
+ + {({ + errors, + handleSubmit, + isSubmitting, + setFieldError, + setFieldTouched, + setFieldValue, + validateForm + }) => ( + + + + ) + }} + />} + innerClassName="change-username-inner" + outerClassName="change-username-outer" + title={} + waiting={isSubmitting} + onSubmit={handleSubmit} + nextButton={} + > +
+ Create a new username + { + setFieldValue('newUsername', e.target.value.substring(0, 30)); + setFieldValue('canValidate', false); + setFieldTouched('newUsername'); + setFieldError('newUsername', null); + }} + /> + { + setFieldValue('newUsernameConfirm', e.target.value.substring(0, 30)); + setFieldTouched('newUsernameConfirm'); + setFieldError('newUsernameConfirm', null); + setFieldValue('canValidate', false); + }} + onBlur={() => { + setFieldValue('canValidate', true).then(validateForm()); + }} + /> + Password + { + setFieldValue('password', e.target.value); + setFieldTouched('password'); + setFieldError('password', null); + }} + /> +
+
)} +
+
+
+
+
+
+ +
+ {adminMessages.map(message => ( +
+
+ {new Date(message.datetime_created).toDateString()} +
+ {/* eslint-disable-next-line react/no-danger */} +
+
+ ))} +
+
+ ); + } + return
; +}; + +BannedSplash.propTypes = { + user: PropTypes.shape({ + username: PropTypes.string, + banned: PropTypes.bool, + token: PropTypes.string + }), + hasSession: PropTypes.bool, + adminMessages: PropTypes.arrayOf(PropTypes.shape({ + id: PropTypes.number.isRequired, + datetimeCreated: PropTypes.string.isRequired, + message: PropTypes.string.isRequired + })), + getAdminMessages: PropTypes.func +}; + +const ConnectedBannedSplash = connect( + state => ({ + user: selectUser(state), + hasSession: selectHasFetchedSession(state), + adminMessages: state.messages.messages && + state.messages.messages.admin && + state.messages.messages.admin.sort((a, b) => + (b.id - a.id) + ) + }), + dispatch => ({ + getAdminMessages: (username, token) => { + dispatch(messageActions.getAdminMessages( + username, token, 0 + )); + } + }) +)(BannedSplash); + + +const WrappedBannedSplash = injectIntl(ConnectedBannedSplash); + +render(, document.getElementById('app'), + {messages: messageActions.messagesReducer}); diff --git a/src/views/bad-username-splash/bad-username-splash.scss b/src/views/bad-username-splash/bad-username-splash.scss new file mode 100644 index 000000000..8a858aa58 --- /dev/null +++ b/src/views/bad-username-splash/bad-username-splash.scss @@ -0,0 +1,134 @@ +@import "../../colors"; +@import "../../frameless"; + +.validation-left { + transform: translate(-20.5rem, 0); +} + +@media #{$intermediate-and-smaller} { + .validation-full-width-input { + box-sizing: border-box; + transform: unset; + margin-bottom: .75rem; + max-width: 100%; + } +} + +.inline{ + display:inline; +} + +#bad-username-splash{ + display: flex; + flex-direction: column; + align-items: center; +} + +#force-account-rename{ + width: 100%; + display: flex; + justify-content: center; + + #force-account-rename-inner{ + max-width: 1094px; + display: flex; + flex-direction: row; + @media only screen and (max-width: 800px) { + flex-direction: column; + } + + text-align: left; + + input{ + box-sizing: border-box; + width: 100%; + } + } + background-color: #575E75; + .col{ + padding: 40px; + flex: 1; + } +} + + +#admin-message-list{ + display: inline-block; + padding: 25px; + max-width: 1094px; + + #admin-message-list-title{ + text-align: left; + } +} + +.admin-message{ + text-align: left; + .admin-message-date{ + color: #575E75; + font-size: 12px; + padding-bottom: 10px; + } + text-align: left; + border-radius: 8px; + padding: 16px; + background-color: #E5F0FF; + margin: 10px; +} + +#force-account-rename-text{ + h1, h3, p{ + color: white; + } +} + +.empty{ + text-align: left; + + .admin-message{ + margin: 10px; + } +} + +.banned-message-box{ + margin: 20px; + text-align: left; +} + +.join-flow-outer-content{ + background-color: white; + border-radius: 16px; + color: #575e75; + max-width: 468px; + min-height: auto !important; + @media only screen and (max-width: 800px) { + margin: auto; + } +} + +.banned-icon{ + margin-right: 10px; + padding-top: 10px; + margin-bottom: -3px; +} + +.modal-flush-bottom-button{ + background-color: #855CD6; +} +.modal-flush-bottom-button:hover{ + background-color: #855CD6; +} + +.join-flow-outer-content{ + border: solid 4px #818698; + border-radius: 21px; +} + +.mt5{ + margin-top: 5px; +} + +.white-underline-link{ + color: white !important; + text-decoration: underline !important; +} \ No newline at end of file diff --git a/src/views/bad-username-splash/blocked-account.svg b/src/views/bad-username-splash/blocked-account.svg new file mode 100644 index 000000000..897a34da9 --- /dev/null +++ b/src/views/bad-username-splash/blocked-account.svg @@ -0,0 +1,3 @@ + + +