mirror of
https://github.com/naturalcrit/naturalcrit.git
synced 2025-10-27 07:29:54 +00:00
move login and account to use the same auth component
This commit is contained in:
parent
37e2f7d7a5
commit
e7ca0615a3
@ -40,6 +40,11 @@ const AccountActions = {
|
||||
});
|
||||
},
|
||||
|
||||
validateUsername: (username) => {
|
||||
const regex = /^(?!.*@).{3,}$/;
|
||||
return regex.test(username);
|
||||
},
|
||||
|
||||
checkUsername: (username) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
request
|
||||
@ -51,26 +56,37 @@ const AccountActions = {
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
rename: (username, newUsername) => {
|
||||
//.put(`http://localhost:8000/api/user/rename`) for local purposes
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
request
|
||||
.put('/rename')
|
||||
.send({ username, newUsername })
|
||||
.end((err, res) => {
|
||||
if (err) return reject(err);
|
||||
rename: (username, newUsername, password) => {
|
||||
console.log('attempting rename');
|
||||
return AccountActions.login(username, password)
|
||||
.then(() => {
|
||||
return new Promise((resolve, reject) => {
|
||||
request
|
||||
.put(`https://homebrewery.naturalcrit.com/api/user/rename`)
|
||||
.set('Homebrewery-Version', '3.16.1')
|
||||
.put('/rename')
|
||||
.send({ username, newUsername })
|
||||
.end((err, res) => {
|
||||
if (err) return reject(err);
|
||||
return resolve(res.body);
|
||||
console.log('correctly renamed, now relogging');
|
||||
AccountActions.removeSession();
|
||||
AccountActions.login(newUsername, password).then(() => {
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 500);
|
||||
});
|
||||
request
|
||||
.put('https://homebrewery.naturalcrit.com/api/user/rename')
|
||||
.set('Homebrewery-Version', '3.16.1')
|
||||
.send({ username, newUsername })
|
||||
.end((err, res) => {
|
||||
if (err) return reject(err);
|
||||
return resolve(res.body);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
return Promise.reject(err);
|
||||
});
|
||||
},
|
||||
|
||||
createSession: (token) => {
|
||||
|
||||
@ -1,32 +1,58 @@
|
||||
const React = require('react');
|
||||
const AccountActions = require('../account.actions.js');
|
||||
const NaturalCritIcon = require('naturalcrit/components/naturalcritLogo.jsx');
|
||||
const RenameForm = require('../loginPage/renameForm.jsx');
|
||||
const AuthForm = require('../loginPage/authForm.jsx'); // Import AuthForm
|
||||
|
||||
class AccountPage extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
showLogin: false
|
||||
showRenameForm: false,
|
||||
processing: false,
|
||||
errors: null,
|
||||
};
|
||||
this.toggleLogin = this.toggleLogin.bind(this);
|
||||
this.handleRenameSuccess = this.handleRenameSuccess.bind(this); // Bind method
|
||||
this.handleRename = this.handleRename.bind(this); // Bind handleRename here
|
||||
this.toggleRenameForm = this.toggleRenameForm.bind(this); // Bind the method here
|
||||
}
|
||||
|
||||
toggleLogin() {
|
||||
this.setState({ showLogin: !this.state.showLogin });
|
||||
toggleRenameForm() {
|
||||
this.setState({ showRenameForm: !this.state.showRenameForm });
|
||||
}
|
||||
|
||||
handleRenameSuccess(newUsername, password) {
|
||||
console.log('handling rename, ', newUsername, password);
|
||||
AccountActions.removeSession();
|
||||
|
||||
AccountActions.login(newUsername, password).then(() => {
|
||||
this.setState({ showLogin: false });
|
||||
window.location.reload();
|
||||
}).catch(error => {
|
||||
console.error('Login failed', error);
|
||||
handleRename(newUsername, password) {
|
||||
const regex = /^(?!.*@).{3,}$/;
|
||||
|
||||
if (!regex.test(newUsername)) {
|
||||
this.setState({
|
||||
processing: false,
|
||||
errors: { msg: 'Username must be at least 3 characters long and not include @!?.' },
|
||||
});
|
||||
return Promise.reject('Invalid username');
|
||||
}
|
||||
|
||||
//if (!confirm('Are you sure you want to rename your account?')) return Promise.reject('User canceled rename');
|
||||
|
||||
this.setState({
|
||||
processing: true,
|
||||
errors: null,
|
||||
});
|
||||
|
||||
return AccountActions.rename(this.props.user.username, newUsername, password)
|
||||
.then(() => {
|
||||
this.setState({
|
||||
processing: false,
|
||||
errors: null,
|
||||
showRenameForm: false,
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
this.setState({
|
||||
processing: false,
|
||||
errors: err,
|
||||
});
|
||||
return Promise.reject(err);
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
@ -51,17 +77,12 @@ class AccountPage extends React.Component {
|
||||
}}>
|
||||
Log Out
|
||||
</button>
|
||||
<button className="rename" onClick={this.toggleLogin}>
|
||||
<button className="rename" onClick={this.toggleRenameForm}>
|
||||
Change my username
|
||||
</button>
|
||||
<br />
|
||||
<br />
|
||||
{this.state.showLogin && (
|
||||
<RenameForm
|
||||
user={this.props.user}
|
||||
onRenameSuccess={this.handleRenameSuccess}
|
||||
/>
|
||||
)}
|
||||
{this.state.showRenameForm && <AuthForm actionType="rename" onSubmit={this.handleRename} />}
|
||||
<small>Upcoming features will include account deletion and username changes.</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
184
client/naturalcrit/loginPage/authForm.jsx
Normal file
184
client/naturalcrit/loginPage/authForm.jsx
Normal file
@ -0,0 +1,184 @@
|
||||
const React = require('react');
|
||||
const cx = require('classnames');
|
||||
const _ = require('lodash');
|
||||
const AccountActions = require('../account.actions');
|
||||
|
||||
const AuthForm = React.createClass({
|
||||
getDefaultProps: function () {
|
||||
return {
|
||||
onSubmit: () => Promise.resolve(),
|
||||
user: null,
|
||||
actionType: 'login', // 'login', 'signup', or 'rename'
|
||||
};
|
||||
},
|
||||
|
||||
getInitialState: function () {
|
||||
return {
|
||||
visible: false,
|
||||
username: this.props.user && this.props.user.username ? this.props.user.username : '',
|
||||
password: '',
|
||||
processing: false,
|
||||
checkingUsername: false,
|
||||
usernameExists: false,
|
||||
errors: null,
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount: function () {
|
||||
console.log('mounting authform');
|
||||
window.document.addEventListener('keydown', this.handleKeyDown);
|
||||
},
|
||||
|
||||
componentWillUnmount: function () {
|
||||
window.document.removeEventListener('keydown', this.handleKeyDown);
|
||||
},
|
||||
|
||||
handleKeyDown: function (e) {
|
||||
if (e.code === 'Enter') this.handleSubmit();
|
||||
},
|
||||
|
||||
handleInputChange: function (field) {
|
||||
return (e) => {
|
||||
this.setState({ [field]: e.target.value }, () => {
|
||||
if (field === 'username') this.checkUsername();
|
||||
});
|
||||
};
|
||||
},
|
||||
|
||||
checkUsername: function () {
|
||||
if (this.state.username === '') return;
|
||||
|
||||
this.setState({
|
||||
checkingUsername: true,
|
||||
});
|
||||
this.debounceCheckUsername(this.state.username);
|
||||
},
|
||||
|
||||
debounceCheckUsername: _.debounce(function () {
|
||||
AccountActions.checkUsername(this.state.username).then((doesExist) => {
|
||||
this.setState({
|
||||
usernameExists: !!doesExist,
|
||||
checkingUsername: false,
|
||||
});
|
||||
});
|
||||
}, 1000),
|
||||
|
||||
isValid: function () {
|
||||
const { username, password, usernameExists, processing } = this.state;
|
||||
const { actionType } = this.props;
|
||||
|
||||
if (processing) return false;
|
||||
|
||||
if (actionType === 'login') return username && password;
|
||||
if (actionType === 'signup' || actionType === 'rename') return username && password && !usernameExists;
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
handleSubmit: function () {
|
||||
const { username, password } = this.state;
|
||||
const { actionType, onSubmit } = this.props;
|
||||
|
||||
if (!this.isValid()) return;
|
||||
|
||||
this.setState({ processing: true, errors: null });
|
||||
|
||||
onSubmit(username, password, actionType)
|
||||
.then(() => this.setState({ processing: false, errors: null }))
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
this.setState({
|
||||
processing: false,
|
||||
errors: err,
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
renderErrors: function () {
|
||||
const { errors } = this.state;
|
||||
if (!errors) return null;
|
||||
|
||||
return <div className="errors">{errors.msg || 'Something went wrong'}</div>;
|
||||
},
|
||||
|
||||
renderUsernameValidation: function () {
|
||||
const { checkingUsername, usernameExists, username } = this.state;
|
||||
if (!username) return null;
|
||||
|
||||
let icon;
|
||||
if (checkingUsername) icon = <i className="fa fa-spinner fa-spin" />;
|
||||
else if (usernameExists) icon = <i className="fa fa-times red" />;
|
||||
else icon = <i className="fa fa-check green" />;
|
||||
|
||||
return <div className="control">{icon}</div>;
|
||||
},
|
||||
|
||||
renderButton: function () {
|
||||
let className = '';
|
||||
let text = '';
|
||||
let icon = '';
|
||||
|
||||
if (this.state.processing) {
|
||||
className = 'processing';
|
||||
text = 'processing';
|
||||
icon = 'fa-spinner fa-spin';
|
||||
} else if (this.props.actionType === 'login') {
|
||||
className = 'login';
|
||||
text = 'login';
|
||||
icon = 'fa-sign-in';
|
||||
} else if (this.props.actionType === 'signup') {
|
||||
className = 'signup';
|
||||
text = 'signup';
|
||||
icon = 'fa-user-plus';
|
||||
} else if (this.props.actionType === 'rename') {
|
||||
className = 'rename';
|
||||
text = 'rename';
|
||||
icon = 'fa-user-plus';
|
||||
}
|
||||
|
||||
return (
|
||||
<button className={cx('action', className)} disabled={!this.isValid()} onClick={this.handleSubmit}>
|
||||
<i className={`fa ${icon}`} />
|
||||
{text}
|
||||
</button>
|
||||
);
|
||||
},
|
||||
|
||||
render: function () {
|
||||
const { actionType } = this.props;
|
||||
const { visible, username, password, processing } = this.state;
|
||||
|
||||
let buttonText;
|
||||
if (processing) buttonText = 'Processing...';
|
||||
else if (actionType === 'login') buttonText = 'Login';
|
||||
else if (actionType === 'signup') buttonText = 'Signup';
|
||||
else buttonText = 'Rename';
|
||||
|
||||
return (
|
||||
<div className={`authForm ${actionType}`}>
|
||||
<label className="field user">
|
||||
Username
|
||||
<input type="text" onChange={this.handleInputChange('username')} value={username} />
|
||||
{this.props.actionType !== 'login' && this.renderUsernameValidation()}
|
||||
</label>
|
||||
|
||||
<label className="field password">
|
||||
Password
|
||||
<input
|
||||
type={cx({ text: visible, password: !visible })}
|
||||
onChange={this.handleInputChange('password')}
|
||||
value={password}
|
||||
/>
|
||||
<div className="control" onClick={() => this.setState({ visible: !visible })}>
|
||||
<i className={cx('fa', { 'fa-eye': !visible, 'fa-eye-slash': visible })} />
|
||||
</div>
|
||||
</label>
|
||||
|
||||
{this.renderErrors()}
|
||||
{this.renderButton()}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = AuthForm;
|
||||
@ -1,38 +1,6 @@
|
||||
.authForm {
|
||||
width: 400px;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
|
||||
.switchView {
|
||||
width: 100%;
|
||||
|
||||
&>div {
|
||||
.animate(background-color);
|
||||
display: inline-block;
|
||||
width: 50%;
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
font-size: 0.8em;
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
|
||||
i {
|
||||
vertical-align: middle;
|
||||
font-size: 2em;
|
||||
}
|
||||
}
|
||||
|
||||
.login:hover,
|
||||
.login.selected {
|
||||
background-color: fade(@blue, 20%);
|
||||
}
|
||||
|
||||
.signup:hover,
|
||||
.signup.selected {
|
||||
background-color: fade(@green, 20%);
|
||||
}
|
||||
}
|
||||
|
||||
.field {
|
||||
display: block;
|
||||
@ -107,7 +75,7 @@
|
||||
background-color: @blue;
|
||||
}
|
||||
|
||||
&.signup {
|
||||
&.signup, &.rename {
|
||||
background-color: @green;
|
||||
}
|
||||
|
||||
@ -117,24 +85,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
button.google {
|
||||
cursor: pointer;
|
||||
width: 191px;
|
||||
height: 46px;
|
||||
border: none;
|
||||
outline: none;
|
||||
background-color: unset;
|
||||
background-image: url('../assets/naturalcrit/styles/btn_google_signin_light_normal_web.png');
|
||||
background-size: contain;
|
||||
|
||||
&:hover {
|
||||
background-image: url('../assets/naturalcrit/styles/btn_google_signin_light_hover_web.png');
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background-image: url('../assets/naturalcrit/styles/btn_google_signin_light_pressed_web.png');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.errors {
|
||||
margin-bottom: 20px;
|
||||
@ -1,9 +1,8 @@
|
||||
const React = require('react');
|
||||
const _ = require('lodash');
|
||||
const cx = require('classnames');
|
||||
|
||||
const NaturalCritIcon = require('naturalcrit/components/naturalcritLogo.jsx');
|
||||
const AccountActions = require('../account.actions.js');
|
||||
const AuthForm = require('./authForm.jsx');
|
||||
const NaturalCritIcon = require('naturalcrit/components/naturalcritLogo.jsx');
|
||||
|
||||
const RedirectLocation = 'NC-REDIRECT-URL';
|
||||
|
||||
@ -16,32 +15,11 @@ const LoginPage = React.createClass({
|
||||
},
|
||||
getInitialState: function () {
|
||||
return {
|
||||
view: 'login', //or 'signup'
|
||||
visible: false,
|
||||
|
||||
username: '',
|
||||
password: '',
|
||||
|
||||
processing: false,
|
||||
checkingUsername: false,
|
||||
view: 'login', // or 'signup'
|
||||
redirecting: false,
|
||||
|
||||
usernameExists: false,
|
||||
|
||||
errors: null,
|
||||
success: false,
|
||||
};
|
||||
},
|
||||
componentDidMount: function () {
|
||||
window.document.addEventListener('keydown', (e) => {
|
||||
if (e.code === 'Enter') this.handleClick();
|
||||
});
|
||||
this.handleRedirectURL();
|
||||
},
|
||||
|
||||
componentWillUnmount: function () {
|
||||
window.document.removeEventListener('keydown', this.handleKeyPress);
|
||||
},
|
||||
|
||||
handleRedirectURL: function () {
|
||||
if (!this.props.redirect) {
|
||||
@ -50,28 +28,6 @@ const LoginPage = React.createClass({
|
||||
return window.sessionStorage.setItem(RedirectLocation, this.props.redirect);
|
||||
},
|
||||
|
||||
handleUserChange: function (e) {
|
||||
this.setState({ username: e.target.value });
|
||||
if (this.props.user && this.props.user.username) return;
|
||||
this.setState(
|
||||
{
|
||||
usernameExists: true,
|
||||
checkingUsername: true,
|
||||
},
|
||||
() => {
|
||||
if (this.state.view === 'signup') this.checkUsername();
|
||||
}
|
||||
);
|
||||
},
|
||||
handlePassChange: function (e) {
|
||||
this.setState({ password: e.target.value });
|
||||
},
|
||||
handleClick: function () {
|
||||
if (!this.isValid()) return;
|
||||
if (this.state.view === 'login') this.login();
|
||||
if (this.state.view === 'signup') this.signup();
|
||||
},
|
||||
|
||||
redirect: function () {
|
||||
if (!this.props.redirect) return (window.location = '/');
|
||||
this.setState(
|
||||
@ -84,109 +40,30 @@ const LoginPage = React.createClass({
|
||||
);
|
||||
},
|
||||
|
||||
login: function () {
|
||||
this.setState({
|
||||
processing: true,
|
||||
errors: null,
|
||||
});
|
||||
AccountActions.login(this.state.username, this.state.password)
|
||||
.then((token) => {
|
||||
this.setState(
|
||||
{
|
||||
processing: false,
|
||||
errors: null,
|
||||
success: true,
|
||||
},
|
||||
this.redirect
|
||||
);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
this.setState({
|
||||
processing: false,
|
||||
errors: err,
|
||||
handleLoginSignup: function (username, password, view) {
|
||||
if (view === 'login') {
|
||||
return AccountActions.login(username, password)
|
||||
.then((token) => {
|
||||
this.setState({ redirecting: true }, this.redirect);
|
||||
})
|
||||
.catch((err) => {
|
||||
this.setState({})
|
||||
console.log(err);
|
||||
return Promise.reject(err);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
logout: function (e) {
|
||||
e.preventDefault();
|
||||
AccountActions.removeSession();
|
||||
window.location.reload();
|
||||
return false;
|
||||
},
|
||||
|
||||
signup: function () {
|
||||
this.setState({
|
||||
processing: true,
|
||||
errors: null,
|
||||
});
|
||||
|
||||
AccountActions.signup(this.state.username, this.state.password)
|
||||
.then((token) => {
|
||||
this.setState(
|
||||
{
|
||||
processing: false,
|
||||
errors: null,
|
||||
success: true,
|
||||
},
|
||||
this.redirect
|
||||
);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
this.setState({
|
||||
processing: false,
|
||||
errors: err,
|
||||
} else if (view === 'signup') {
|
||||
return AccountActions.signup(username, password)
|
||||
.then((token) => {
|
||||
this.setState({ redirecting: true }, this.redirect);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
return Promise.reject(err);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
checkUsername: function () {
|
||||
if (this.state.username === '') return;
|
||||
const regex = /^(?!.*@).{3,}$/;
|
||||
|
||||
if (!regex.test(this.state.username)) {
|
||||
this.setState({
|
||||
processing: false,
|
||||
errors: { username: 'Username must be at least 3 characters long.' },
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
checkingUsername: true,
|
||||
});
|
||||
this.debounceCheckUsername();
|
||||
},
|
||||
|
||||
debounceCheckUsername: _.debounce(function () {
|
||||
AccountActions.checkUsername(this.state.username).then((doesExist) => {
|
||||
this.setState({
|
||||
usernameExists: !!doesExist,
|
||||
checkingUsername: false,
|
||||
});
|
||||
});
|
||||
}, 1000),
|
||||
|
||||
handleChangeView: function (newView) {
|
||||
this.setState(
|
||||
{
|
||||
view: newView,
|
||||
errors: null,
|
||||
},
|
||||
this.checkUsername
|
||||
);
|
||||
},
|
||||
|
||||
isValid: function () {
|
||||
if (this.state.processing) return false;
|
||||
|
||||
if (this.state.view === 'login') {
|
||||
return this.state.username && this.state.password;
|
||||
} else if (this.state.view === 'signup') {
|
||||
return this.state.username && this.state.password && !this.state.usernameExists;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
|
||||
linkGoogle: function () {
|
||||
if (this.props.user) {
|
||||
@ -208,88 +85,14 @@ const LoginPage = React.createClass({
|
||||
window.location.href = '/auth/google';
|
||||
},
|
||||
|
||||
// loginGoogle : function(){
|
||||
// this.setState({
|
||||
// processing : true,
|
||||
// errors : null
|
||||
// });
|
||||
// console.log("about to log into google!");
|
||||
// AccountActions.loginGoogle();
|
||||
// },
|
||||
|
||||
// console.log("about to start login");
|
||||
// AccountActions.login(this.state.username, this.state.password)
|
||||
// .then((token) => {
|
||||
// this.setState({
|
||||
// processing : false,
|
||||
// errors : null,
|
||||
// success : true
|
||||
// }, this.redirect);
|
||||
// })
|
||||
// .catch((err) => {
|
||||
// console.log(err);
|
||||
// this.setState({
|
||||
// processing : false,
|
||||
// errors : err
|
||||
// });
|
||||
// });
|
||||
// },
|
||||
|
||||
renderErrors: function () {
|
||||
if (!this.state.errors) return;
|
||||
if (this.state.errors.msg) return <div className="errors">{this.state.errors.msg}</div>;
|
||||
return <div className="errors">Something went wrong</div>;
|
||||
},
|
||||
|
||||
renderUsernameValidation: function () {
|
||||
if (this.state.view === 'login') return;
|
||||
|
||||
let icon = null;
|
||||
|
||||
if (this.state.checkingUsername) {
|
||||
icon = <i className="fa fa-spinner fa-spin" />;
|
||||
} else if (!this.state.username || this.state.usernameExists) {
|
||||
icon = <i className="fa fa-times red" />;
|
||||
} else if (!this.state.usernameExists) {
|
||||
icon = <i className="fa fa-check green" />;
|
||||
}
|
||||
|
||||
return <div className="control">{icon}</div>;
|
||||
},
|
||||
|
||||
renderButton: function () {
|
||||
let className = '';
|
||||
let text = '';
|
||||
let icon = '';
|
||||
|
||||
if (this.state.processing) {
|
||||
className = 'processing';
|
||||
text = 'processing';
|
||||
icon = 'fa-spinner fa-spin';
|
||||
} else if (this.state.view === 'login') {
|
||||
className = 'login';
|
||||
text = 'login';
|
||||
icon = 'fa-sign-in';
|
||||
} else if (this.state.view === 'signup') {
|
||||
className = 'signup';
|
||||
text = 'signup';
|
||||
icon = 'fa-user-plus';
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
className={cx('action', className)}
|
||||
disabled={!this.isValid() || (this.props.user && this.props.user.username)}
|
||||
onClick={this.handleClick}>
|
||||
<i className={`fa ${icon}`} />
|
||||
{text}
|
||||
</button>
|
||||
);
|
||||
handleChangeView: function (newView) {
|
||||
this.setState({
|
||||
view: newView,
|
||||
});
|
||||
},
|
||||
|
||||
renderLoggedIn: function () {
|
||||
if (!this.props.user) return;
|
||||
let loggedInGoogle = '';
|
||||
if (!this.props.user.googleId) {
|
||||
return (
|
||||
<small>
|
||||
@ -312,65 +115,28 @@ const LoginPage = React.createClass({
|
||||
},
|
||||
|
||||
render: function () {
|
||||
console.log(this.props.redirect);
|
||||
return (
|
||||
<div className="loginPage">
|
||||
<NaturalCritIcon />
|
||||
|
||||
<div className="authForm">
|
||||
<div className="content">
|
||||
<div className="switchView">
|
||||
<div
|
||||
className={cx('login', { 'selected': this.state.view === 'login' })}
|
||||
className={cx('login', { selected: this.state.view === 'login' })}
|
||||
onClick={this.handleChangeView.bind(null, 'login')}>
|
||||
<i className="fa fa-sign-in" /> Login
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={cx('signup', { 'selected': this.state.view === 'signup' })}
|
||||
className={cx('signup', { selected: this.state.view === 'signup' })}
|
||||
onClick={this.handleChangeView.bind(null, 'signup')}>
|
||||
<i className="fa fa-user-plus" /> Signup
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label className="field user">
|
||||
username
|
||||
<input
|
||||
type="text"
|
||||
title={this.state.view === 'signup' ? 'Min 3 characters, and cannot contain ?!¿@ .' : ''}
|
||||
onChange={this.handleUserChange}
|
||||
value={this.state.username}
|
||||
/>
|
||||
{this.renderUsernameValidation()}
|
||||
{this.state.usernameExists && !this.state.checkingUsername && this.state.view === 'signup' ? (
|
||||
<div className="userExists">User with that name already exists</div>
|
||||
) : null}
|
||||
</label>
|
||||
|
||||
<label className="field password">
|
||||
password
|
||||
<input
|
||||
type={cx({ text: this.state.visible, password: !this.state.visible })}
|
||||
onChange={this.handlePassChange}
|
||||
value={this.state.password}
|
||||
/>
|
||||
<div
|
||||
className="control"
|
||||
onClick={() => {
|
||||
this.setState({ visible: !this.state.visible });
|
||||
}}>
|
||||
<i
|
||||
className={cx('fa', {
|
||||
'fa-eye': !this.state.visible,
|
||||
'fa-eye-slash': this.state.visible,
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
</label>
|
||||
{this.renderErrors()}
|
||||
{this.renderButton()}
|
||||
<AuthForm actionType={this.state.view} onSubmit={this.handleLoginSignup} />
|
||||
<div className="divider">⎯⎯ OR ⎯⎯</div>
|
||||
<button className="google" onClick={this.linkGoogle}></button>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
|
||||
@ -2,16 +2,61 @@
|
||||
text-align: center;
|
||||
padding-top: 30px;
|
||||
|
||||
.authForm {
|
||||
.content {
|
||||
width: 400px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
padding-top: 50px;
|
||||
}
|
||||
|
||||
.divider {
|
||||
font-size: 1em;
|
||||
font-weight: 800;
|
||||
color: black;
|
||||
text-transform: uppercase;
|
||||
padding: 12px 20px 10px;
|
||||
.switchView {
|
||||
width: 100%;
|
||||
|
||||
&>div {
|
||||
.animate(background-color);
|
||||
display: inline-block;
|
||||
width: 50%;
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
font-size: 0.8em;
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
|
||||
i {
|
||||
vertical-align: middle;
|
||||
font-size: 2em;
|
||||
}
|
||||
}
|
||||
|
||||
.login:hover,
|
||||
.login.selected {
|
||||
background-color: fade(@blue, 20%);
|
||||
}
|
||||
|
||||
.signup:hover,
|
||||
.signup.selected {
|
||||
background-color: fade(@green, 20%);
|
||||
}
|
||||
}
|
||||
|
||||
button.google {
|
||||
cursor: pointer;
|
||||
width: 191px;
|
||||
height: 46px;
|
||||
border: none;
|
||||
outline: none;
|
||||
background-color: unset;
|
||||
background-image: url('../assets/naturalcrit/styles/btn_google_signin_light_normal_web.png');
|
||||
background-size: contain;
|
||||
|
||||
&:hover {
|
||||
background-image: url('../assets/naturalcrit/styles/btn_google_signin_light_hover_web.png');
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background-image: url('../assets/naturalcrit/styles/btn_google_signin_light_pressed_web.png');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+.accountButton.login {
|
||||
|
||||
@ -1,221 +0,0 @@
|
||||
const React = require('react');
|
||||
const _ = require('lodash');
|
||||
const cx = require('classnames');
|
||||
|
||||
const NaturalCritIcon = require('naturalcrit/components/naturalcritLogo.jsx');
|
||||
const AccountActions = require('../account.actions.js');
|
||||
|
||||
const RedirectLocation = 'NC-REDIRECT-URL';
|
||||
|
||||
const RenameForm = React.createClass({
|
||||
getDefaultProps: function () {
|
||||
return {
|
||||
user: null,
|
||||
onRenameSuccess: () => {},
|
||||
};
|
||||
},
|
||||
getInitialState: function () {
|
||||
return {
|
||||
visible: false,
|
||||
|
||||
newUsername: '',
|
||||
password: '',
|
||||
|
||||
processing: false,
|
||||
checkingUsername: false,
|
||||
redirecting: false,
|
||||
|
||||
usernameExists: false,
|
||||
errors: null,
|
||||
success: false,
|
||||
};
|
||||
},
|
||||
componentDidMount: function () {
|
||||
window.document.addEventListener('keydown', (e) => {
|
||||
if (e.code === 'Enter') this.login();
|
||||
});
|
||||
},
|
||||
|
||||
componentWillUnmount: function () {
|
||||
window.document.removeEventListener('keydown', this.handleKeyPress);
|
||||
},
|
||||
|
||||
handleUserChange: function (e) {
|
||||
this.setState({ username: e.target.value });
|
||||
this.setState(
|
||||
{
|
||||
usernameExists: true,
|
||||
checkingUsername: true,
|
||||
},
|
||||
() => {
|
||||
if (this.state.view === 'signup') this.checkUsername();
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
handleNewUserChange: function (e) {
|
||||
this.setState({ username: this.props.user.username, newUsername: e.target.value });
|
||||
this.setState({
|
||||
usernameExists: true,
|
||||
checkingUsername: true,
|
||||
});
|
||||
this.checkUsername();
|
||||
},
|
||||
|
||||
handlePassChange: function (e) {
|
||||
this.setState({ password: e.target.value });
|
||||
},
|
||||
|
||||
login: function () {
|
||||
const regex = /^(?!.*@).{3,}$/;
|
||||
|
||||
if (!regex.test(this.state.newUsername)) {
|
||||
this.setState({
|
||||
processing: false,
|
||||
errors: { msg: 'Username must be at least 3 characters long and not include @!?.' },
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!confirm('Are you sure you want to rename your account?')) return;
|
||||
this.setState({
|
||||
processing: true,
|
||||
errors: null,
|
||||
});
|
||||
AccountActions.login(this.props.user.username, this.state.password)
|
||||
.then((token) => {
|
||||
this.setState({
|
||||
processing: false,
|
||||
errors: null,
|
||||
success: true,
|
||||
});
|
||||
//check if username exists
|
||||
|
||||
if (this.state.newUsername) {
|
||||
AccountActions.rename(this.props.user.username, this.state.newUsername)
|
||||
.then((res) => {
|
||||
this.props.onRenameSuccess(this.state.newUsername, this.state.password);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
this.setState({
|
||||
processing: false,
|
||||
errors: err,
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
this.setState({
|
||||
processing: false,
|
||||
errors: err,
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
checkUsername: function () {
|
||||
if (this.state.newUsername === '') return;
|
||||
|
||||
this.setState({
|
||||
checkingUsername: true,
|
||||
});
|
||||
this.debounceCheckUsername(this.state.newUsername);
|
||||
},
|
||||
|
||||
debounceCheckUsername: _.debounce(function () {
|
||||
AccountActions.checkUsername(this.state.newUsername).then((doesExist) => {
|
||||
this.setState({
|
||||
usernameExists: !!doesExist,
|
||||
checkingUsername: false,
|
||||
});
|
||||
});
|
||||
}, 1000),
|
||||
|
||||
isValid: function () {
|
||||
if (this.state.processing) return false;
|
||||
return this.state.newUsername && this.state.password && !this.state.usernameExists;
|
||||
},
|
||||
|
||||
renderErrors: function () {
|
||||
if (!this.state.errors) return;
|
||||
if (this.state.errors.msg) return <div className="errors">{this.state.errors.msg}</div>;
|
||||
return <div className="errors">Something went wrong</div>;
|
||||
},
|
||||
|
||||
renderUsernameValidation: function () {
|
||||
let icon = null;
|
||||
|
||||
if (this.state.checkingUsername) {
|
||||
icon = <i className="fa fa-spinner fa-spin" />;
|
||||
} else if (!this.state.newUsername || this.state.usernameExists) {
|
||||
icon = <i className="fa fa-times red" />;
|
||||
} else if (!this.state.usernameExists) {
|
||||
icon = <i className="fa fa-check green" />;
|
||||
}
|
||||
|
||||
return <div className="control">{icon}</div>;
|
||||
},
|
||||
|
||||
renderButton: function () {
|
||||
let className = '';
|
||||
let text = '';
|
||||
let icon = '';
|
||||
|
||||
if (this.state.processing) {
|
||||
className = 'processing';
|
||||
text = 'processing';
|
||||
icon = 'fa-spinner fa-spin';
|
||||
} else {
|
||||
className = 'signup';
|
||||
text = 'Rename';
|
||||
icon = 'fa-signature';
|
||||
}
|
||||
|
||||
return (
|
||||
<button className={cx('action', className)} disabled={!this.isValid()} onClick={this.login}>
|
||||
<i className={`fa ${icon}`} />
|
||||
{text}
|
||||
</button>
|
||||
);
|
||||
},
|
||||
|
||||
render: function () {
|
||||
return (
|
||||
<div className="authForm">
|
||||
<label className="field user">
|
||||
new username
|
||||
<input type="text" onChange={this.handleNewUserChange} value={this.state.newUsername} />
|
||||
{this.renderUsernameValidation()}
|
||||
{this.state.usernameExists && !this.state.checkingUsername ? (
|
||||
<div className="userExists">User with that name already exists</div>
|
||||
) : null}
|
||||
</label>
|
||||
|
||||
<label className="field password">
|
||||
password
|
||||
<input
|
||||
type={cx({ text: this.state.visible, password: !this.state.visible })}
|
||||
onChange={this.handlePassChange}
|
||||
value={this.state.password}
|
||||
/>
|
||||
<div
|
||||
className="control"
|
||||
onClick={() => {
|
||||
this.setState({ visible: !this.state.visible });
|
||||
}}>
|
||||
<i
|
||||
className={cx('fa', {
|
||||
'fa-eye': !this.state.visible,
|
||||
'fa-eye-slash': this.state.visible,
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
</label>
|
||||
{this.renderErrors()}
|
||||
{this.renderButton()}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = RenameForm;
|
||||
Loading…
Reference in New Issue
Block a user