Added displayUsername and username update functionality (#35699)
* Added displayUsername and username update functionality * fix: move username assignment to safe place moved the username assignment down a statement so that it doesn't cause exception * fix: handle missing username or displayUsername * refactor: remove redundant code
This commit is contained in:
committed by
Oliver Eyton-Williams
parent
0f5b9f8764
commit
e154f38118
@ -127,6 +127,9 @@ function nextTick(fn) {
|
||||
const getRandomNumber = () => Math.random();
|
||||
|
||||
function populateRequiredFields(user) {
|
||||
// by default, the displayUsername will have
|
||||
// the same value as the username
|
||||
user.displayUsername = user.username;
|
||||
user.username = user.username.trim().toLowerCase();
|
||||
user.email =
|
||||
typeof user.email === 'string'
|
||||
@ -347,6 +350,7 @@ export default function(User) {
|
||||
if (!username && (!email || !isEmail(email))) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
username = username.toLowerCase();
|
||||
log('checking existence');
|
||||
|
||||
// check to see if username is on blacklist
|
||||
@ -395,6 +399,7 @@ export default function(User) {
|
||||
cb(null, {});
|
||||
});
|
||||
}
|
||||
username = username.toLowerCase();
|
||||
return User.findOne({ where: { username } }, (err, user) => {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
@ -724,13 +729,13 @@ export default function(User) {
|
||||
|
||||
User.prototype.updateMyUsername = function updateMyUsername(newUsername) {
|
||||
return Observable.defer(() => {
|
||||
const isOwnUsername = isTheSame(newUsername, this.username);
|
||||
const isOwnUsername = isTheSame(newUsername.toLowerCase(), this.username);
|
||||
if (isOwnUsername) {
|
||||
return Observable.of(dedent`
|
||||
${newUsername} is already associated with this account.
|
||||
`);
|
||||
}
|
||||
return Observable.fromPromise(User.doesExist(newUsername));
|
||||
return Observable.fromPromise(User.doesExist(newUsername.toLowerCase()));
|
||||
}).flatMap(boolOrMessage => {
|
||||
if (typeof boolOrMessage === 'string') {
|
||||
return Observable.of(boolOrMessage);
|
||||
@ -741,14 +746,20 @@ export default function(User) {
|
||||
`);
|
||||
}
|
||||
|
||||
const usernameUpdate = new Promise((resolve, reject) =>
|
||||
this.updateAttribute('username', newUsername, err => {
|
||||
const usernameUpdate = new Promise((resolve, reject) => {
|
||||
this.updateAttributes(
|
||||
{
|
||||
username: newUsername.toLowerCase(),
|
||||
displayUsername: newUsername
|
||||
},
|
||||
err => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
return resolve();
|
||||
})
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return Observable.fromPromise(usernameUpdate).map(
|
||||
() => dedent`
|
||||
|
@ -74,6 +74,10 @@
|
||||
},
|
||||
"require": true
|
||||
},
|
||||
"displayUsername": {
|
||||
"type": "string",
|
||||
"default":""
|
||||
},
|
||||
"about": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
|
@ -194,7 +194,7 @@ function createUpdateMyUsername(app) {
|
||||
user,
|
||||
body: { username }
|
||||
} = req;
|
||||
if (username === user.username) {
|
||||
if (username.toLowerCase() === user.username) {
|
||||
return res.json({
|
||||
type: 'info',
|
||||
message: 'Username is already associated with this account'
|
||||
@ -209,7 +209,7 @@ function createUpdateMyUsername(app) {
|
||||
});
|
||||
}
|
||||
|
||||
const exists = await User.doesExist(username);
|
||||
const exists = await User.doesExist(username.toLowerCase());
|
||||
|
||||
if (exists) {
|
||||
return res.json({
|
||||
@ -218,7 +218,9 @@ function createUpdateMyUsername(app) {
|
||||
});
|
||||
}
|
||||
|
||||
return user.updateAttribute('username', username, err => {
|
||||
return user.updateAttributes(
|
||||
{ username: username.toLowerCase(), displayUsername: username },
|
||||
err => {
|
||||
if (err) {
|
||||
res.status(500).json(standardErrorMessage);
|
||||
return next(err);
|
||||
@ -227,7 +229,8 @@ function createUpdateMyUsername(app) {
|
||||
type: 'success',
|
||||
message: `We have updated your username to ${username}`
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -37,6 +37,7 @@ function createProfileAttributesFromGithub(profile) {
|
||||
return {
|
||||
name,
|
||||
username: username.toLowerCase(),
|
||||
displayUsername: username,
|
||||
location,
|
||||
bio,
|
||||
website,
|
||||
|
@ -11,6 +11,7 @@ export const publicUserProps = [
|
||||
'about',
|
||||
'calendar',
|
||||
'completedChallenges',
|
||||
'displayUsername',
|
||||
'githubProfile',
|
||||
'isApisMicroservicesCert',
|
||||
'isBackEndCert',
|
||||
|
@ -22,6 +22,7 @@ const propTypes = {
|
||||
navigate: PropTypes.func.isRequired,
|
||||
requestedUser: PropTypes.shape({
|
||||
username: PropTypes.string,
|
||||
displayUsername: PropTypes.string,
|
||||
profileUI: PropTypes.object
|
||||
}),
|
||||
showLoading: PropTypes.bool
|
||||
|
@ -48,6 +48,7 @@ const propTypes = {
|
||||
files: PropTypes.array
|
||||
})
|
||||
),
|
||||
displayUsername: PropTypes.string,
|
||||
email: PropTypes.string,
|
||||
githubProfile: PropTypes.string,
|
||||
is2018DataVisCert: PropTypes.bool,
|
||||
@ -121,6 +122,7 @@ export function ShowSettings(props) {
|
||||
toggleNightMode,
|
||||
user: {
|
||||
completedChallenges,
|
||||
displayUsername,
|
||||
email,
|
||||
is2018DataVisCert,
|
||||
isApisMicroservicesCert,
|
||||
@ -191,9 +193,12 @@ export function ShowSettings(props) {
|
||||
</Button>
|
||||
</FullWidthRow>
|
||||
<Spacer />
|
||||
<h1 className='text-center' style={{ overflowWrap: 'break-word' }}>
|
||||
{`Account Settings for ${username}`}
|
||||
</h1>
|
||||
<h1
|
||||
className='text-center'
|
||||
style={{ overflowWrap: 'break-word' }}
|
||||
>{`Account Settings for ${
|
||||
displayUsername ? displayUsername : username
|
||||
}`}</h1>
|
||||
<About
|
||||
about={about}
|
||||
currentTheme={theme}
|
||||
@ -203,7 +208,7 @@ export function ShowSettings(props) {
|
||||
points={points}
|
||||
submitNewAbout={submitNewAbout}
|
||||
toggleNightMode={toggleNightMode}
|
||||
username={username}
|
||||
username={displayUsername ? displayUsername : username}
|
||||
/>
|
||||
<Spacer />
|
||||
<Privacy />
|
||||
|
@ -27,6 +27,7 @@ const propTypes = {
|
||||
showPortfolio: PropTypes.bool,
|
||||
showTimeLine: PropTypes.bool
|
||||
}),
|
||||
displayUsername: PropTypes.string,
|
||||
calendar: PropTypes.object,
|
||||
streak: PropTypes.shape({
|
||||
current: PropTypes.number,
|
||||
@ -121,13 +122,15 @@ function renderProfile(user) {
|
||||
picture,
|
||||
portfolio,
|
||||
about,
|
||||
yearsTopContributor
|
||||
yearsTopContributor,
|
||||
displayUsername
|
||||
} = user;
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<Camper
|
||||
about={showAbout ? about : null}
|
||||
displayUsername={displayUsername}
|
||||
githubProfile={githubProfile}
|
||||
isGithub={isGithub}
|
||||
isLinkedIn={isLinkedIn}
|
||||
@ -147,7 +150,11 @@ function renderProfile(user) {
|
||||
{showCerts ? <Certifications username={username} /> : null}
|
||||
{showPortfolio ? <Portfolio portfolio={portfolio} /> : null}
|
||||
{showTimeLine ? (
|
||||
<Timeline completedMap={completedChallenges} username={username} />
|
||||
<Timeline
|
||||
completedMap={completedChallenges}
|
||||
displayUsername={displayUsername}
|
||||
username={username}
|
||||
/>
|
||||
) : null}
|
||||
<Spacer />
|
||||
</Fragment>
|
||||
|
@ -11,6 +11,7 @@ import './camper.css';
|
||||
|
||||
const propTypes = {
|
||||
about: PropTypes.string,
|
||||
displayUsername: PropTypes.string,
|
||||
githubProfile: PropTypes.string,
|
||||
isGithub: PropTypes.bool,
|
||||
isLinkedIn: PropTypes.bool,
|
||||
@ -48,6 +49,7 @@ function joinArray(array) {
|
||||
function Camper({
|
||||
name,
|
||||
username,
|
||||
displayUsername,
|
||||
location,
|
||||
points,
|
||||
picture,
|
||||
@ -74,7 +76,7 @@ function Camper({
|
||||
/>
|
||||
) : (
|
||||
<Image
|
||||
alt={username + "'s avatar"}
|
||||
alt={displayUsername ? displayUsername : username + "'s avatar"}
|
||||
className='avatar'
|
||||
responsive={true}
|
||||
src={picture}
|
||||
@ -99,7 +101,9 @@ function Camper({
|
||||
website={website}
|
||||
/>
|
||||
<br />
|
||||
<h2 className='text-center username'>@{username}</h2>
|
||||
<h2 className='text-center username'>
|
||||
@{displayUsername ? displayUsername : username}
|
||||
</h2>
|
||||
{name && <p className='text-center name'>{name}</p>}
|
||||
{location && <p className='text-center location'>{location}</p>}
|
||||
{about && <p className='bio text-center'>{about}</p>}
|
||||
|
@ -27,6 +27,7 @@ const propTypes = {
|
||||
)
|
||||
})
|
||||
),
|
||||
displayUsername: PropTypes.string,
|
||||
username: PropTypes.string
|
||||
};
|
||||
|
||||
@ -130,6 +131,7 @@ class TimelineInner extends Component {
|
||||
render() {
|
||||
const {
|
||||
completedMap,
|
||||
displayUsername,
|
||||
idToNameMap,
|
||||
username,
|
||||
sortedTimeline,
|
||||
@ -171,9 +173,9 @@ class TimelineInner extends Component {
|
||||
>
|
||||
<Modal.Header closeButton={true}>
|
||||
<Modal.Title id='contained-modal-title'>
|
||||
{`${username}'s Solution to ${
|
||||
idToNameMap.get(id).challengeTitle
|
||||
}`}
|
||||
{`${
|
||||
displayUsername ? displayUsername : username
|
||||
}'s Solution to ${idToNameMap.get(id).challengeTitle}`}
|
||||
</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
|
@ -20,6 +20,7 @@ import FullWidthRow from '../helpers/FullWidthRow';
|
||||
import { isValidUsername } from '../../../../utils/validate';
|
||||
|
||||
const propTypes = {
|
||||
displayUsername: PropTypes.string,
|
||||
isValidUsername: PropTypes.bool,
|
||||
submitNewUsername: PropTypes.func.isRequired,
|
||||
username: PropTypes.string,
|
||||
@ -54,6 +55,9 @@ class UsernameSettings extends Component {
|
||||
this.state = {
|
||||
isFormPristine: true,
|
||||
formValue: props.username,
|
||||
formDisplayValue: props.displayUsername
|
||||
? props.displayUsername
|
||||
: props.username,
|
||||
characterValidation: { valid: false, error: null },
|
||||
submitClicked: false,
|
||||
isUserNew: tempUserRegex.test(props.username)
|
||||
@ -84,21 +88,23 @@ class UsernameSettings extends Component {
|
||||
e.preventDefault();
|
||||
const { submitNewUsername } = this.props;
|
||||
const {
|
||||
formValue,
|
||||
formDisplayValue,
|
||||
characterValidation: { valid }
|
||||
} = this.state;
|
||||
|
||||
return this.setState({ submitClicked: true }, () =>
|
||||
valid ? submitNewUsername(formValue) : null
|
||||
valid ? submitNewUsername(formDisplayValue) : null
|
||||
);
|
||||
}
|
||||
|
||||
handleChange(e) {
|
||||
e.preventDefault();
|
||||
const { username, validateUsername } = this.props;
|
||||
const newValue = e.target.value.toLowerCase();
|
||||
const newDisplayUsernameValue = e.target.value;
|
||||
const newValue = newDisplayUsernameValue.toLowerCase();
|
||||
return this.setState(
|
||||
{
|
||||
formDisplayValue: newDisplayUsernameValue,
|
||||
formValue: newValue,
|
||||
isFormPristine: username === newValue,
|
||||
characterValidation: this.validateFormInput(newValue),
|
||||
@ -160,7 +166,7 @@ class UsernameSettings extends Component {
|
||||
render() {
|
||||
const {
|
||||
isFormPristine,
|
||||
formValue,
|
||||
formDisplayValue,
|
||||
characterValidation: { valid, error },
|
||||
submitClicked
|
||||
} = this.state;
|
||||
@ -177,7 +183,7 @@ class UsernameSettings extends Component {
|
||||
<FormControl
|
||||
name='username-settings'
|
||||
onChange={this.handleChange}
|
||||
value={formValue}
|
||||
value={formDisplayValue}
|
||||
/>
|
||||
</FormGroup>
|
||||
</FullWidthRow>
|
||||
|
@ -20,7 +20,11 @@ function* fetchSessionUser() {
|
||||
} = yield call(getSessionUser);
|
||||
const appUser = user[result] || {};
|
||||
yield put(
|
||||
fetchUserComplete({ user: appUser, username: result, sessionMeta })
|
||||
fetchUserComplete({
|
||||
user: appUser,
|
||||
username: result,
|
||||
sessionMeta
|
||||
})
|
||||
);
|
||||
} catch (e) {
|
||||
yield put(fetchUserError(e));
|
||||
|
@ -45,6 +45,7 @@ MongoClient.connect(MONGOHQ_URL, { useNewUrlParser: true }, function(
|
||||
isBanned: false,
|
||||
isCheater: false,
|
||||
username: 'developmentuser',
|
||||
displayUsername: 'DevelopmentUser',
|
||||
about: '',
|
||||
name: 'Development User',
|
||||
location: '',
|
||||
|
Reference in New Issue
Block a user