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..4c5e2e40e
--- /dev/null
+++ b/src/views/bad-username-splash/bad-username-splash.jsx
@@ -0,0 +1,293 @@
+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 @@
+