Phyks (Lucas Verney)
d8a7d4f66a
Full rework of webplayer. Webplayer is back to its previous working state, and ready for further improvements.
218 lines
9.1 KiB
JavaScript
218 lines
9.1 KiB
JavaScript
// NPM imports
|
|
import React, { Component, PropTypes } from "react";
|
|
import CSSModules from "react-css-modules";
|
|
import { defineMessages, injectIntl, intlShape, FormattedMessage } from "react-intl";
|
|
import FontAwesome from "react-fontawesome";
|
|
|
|
// Local imports
|
|
import { i18nRecord } from "../models/i18n";
|
|
import { messagesMap } from "../utils";
|
|
|
|
// Translations
|
|
import APIMessages from "../locales/messagesDescriptors/api";
|
|
import messages from "../locales/messagesDescriptors/Login";
|
|
|
|
// Styles
|
|
import css from "../styles/Login.scss";
|
|
|
|
// Define translations
|
|
const loginMessages = defineMessages(messagesMap(Array.concat([], APIMessages, messages)));
|
|
|
|
|
|
/**
|
|
* Login form component
|
|
*/
|
|
class LoginFormCSSIntl extends Component {
|
|
constructor(props) {
|
|
super(props);
|
|
this.handleSubmit = this.handleSubmit.bind(this); // bind this to handleSubmit
|
|
}
|
|
|
|
/**
|
|
* Set an error on a form element.
|
|
*
|
|
* @param formGroup A form element.
|
|
* @param hasError Whether or not an error should be set.
|
|
*
|
|
* @return True if an error is set, false otherwise
|
|
*/
|
|
setError(formGroup, hasError) {
|
|
if (hasError) {
|
|
// If error is true, then add error class
|
|
formGroup.classList.add("has-error");
|
|
formGroup.classList.remove("has-success");
|
|
return true;
|
|
}
|
|
// Else, drop it and put success class
|
|
formGroup.classList.remove("has-error");
|
|
formGroup.classList.add("has-success");
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Form submission handler.
|
|
*
|
|
* @param e JS Event.
|
|
*/
|
|
handleSubmit(e) {
|
|
e.preventDefault();
|
|
|
|
// Don't handle submit if already logging in
|
|
if (this.props.isAuthenticating) {
|
|
return;
|
|
}
|
|
|
|
// Get field values
|
|
const username = this.refs.username.value.trim();
|
|
const password = this.refs.password.value.trim();
|
|
const endpoint = this.refs.endpoint.value.trim();
|
|
const rememberMe = this.refs.rememberMe.checked;
|
|
|
|
// Check for errors on each field
|
|
let hasError = this.setError(this.refs.usernameFormGroup, !username);
|
|
hasError |= this.setError(this.refs.passwordFormGroup, !password);
|
|
hasError |= this.setError(this.refs.endpointFormGroup, !endpoint);
|
|
|
|
if (!hasError) {
|
|
// Submit if no error is found
|
|
this.props.onSubmit(username, password, endpoint, rememberMe);
|
|
}
|
|
}
|
|
|
|
componentDidUpdate() {
|
|
if (this.props.error) {
|
|
// On unsuccessful login, set error classes and shake the form
|
|
$(this.refs.loginForm).shake(3, 10, 300);
|
|
this.setError(this.refs.usernameFormGroup, this.props.error);
|
|
this.setError(this.refs.passwordFormGroup, this.props.error);
|
|
this.setError(this.refs.endpointFormGroup, this.props.error);
|
|
}
|
|
}
|
|
|
|
render() {
|
|
const {formatMessage} = this.props.intl;
|
|
|
|
// Handle info message
|
|
let infoMessage = this.props.info;
|
|
if (this.props.info && this.props.info instanceof i18nRecord) {
|
|
infoMessage = (
|
|
<FormattedMessage {...loginMessages[this.props.info.id]} values={ this.props.info.values} />
|
|
);
|
|
}
|
|
|
|
// Handle error message
|
|
let errorMessage = this.props.error;
|
|
if (this.props.error && this.props.error instanceof i18nRecord) {
|
|
errorMessage = (
|
|
<FormattedMessage {...loginMessages[this.props.error.id]} values={ this.props.error.values} />
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div>
|
|
{
|
|
this.props.error ?
|
|
<div className="row">
|
|
<div className="alert alert-danger" id="loginFormError" role="alert">
|
|
<p>
|
|
<FontAwesome name="exclamation" aria-hidden="true" /> { errorMessage }
|
|
</p>
|
|
</div>
|
|
</div>
|
|
: null
|
|
}
|
|
{
|
|
this.props.info ?
|
|
<div className="row">
|
|
<div className="alert alert-info" id="loginFormInfo" role="alert">
|
|
<p>{ infoMessage }</p>
|
|
</div>
|
|
</div>
|
|
: null
|
|
}
|
|
<div className="row">
|
|
<form className="col-sm-9 col-sm-offset-1 col-md-6 col-md-offset-3 text-left form-horizontal login" onSubmit={this.handleSubmit} ref="loginForm" aria-describedby="loginFormInfo loginFormError">
|
|
<div className="row">
|
|
<div className="form-group" ref="usernameFormGroup">
|
|
<div className="col-xs-12">
|
|
<input type="text" className="form-control" ref="username" aria-label={formatMessage(loginMessages["app.login.username"])} placeholder={formatMessage(loginMessages["app.login.username"])} autoFocus defaultValue={this.props.username} />
|
|
</div>
|
|
</div>
|
|
<div className="form-group" ref="passwordFormGroup">
|
|
<div className="col-xs-12">
|
|
<input type="password" className="form-control" ref="password" aria-label={formatMessage(loginMessages["app.login.password"])} placeholder={formatMessage(loginMessages["app.login.password"])} />
|
|
</div>
|
|
</div>
|
|
<div className="form-group" ref="endpointFormGroup">
|
|
<div className="col-xs-12">
|
|
<input type="text" className="form-control" ref="endpoint" aria-label={formatMessage(loginMessages["app.login.endpointInputAriaLabel"])} placeholder="http://ampache.example.com" defaultValue={this.props.endpoint} />
|
|
</div>
|
|
</div>
|
|
<div className="form-group">
|
|
<div className="col-xs-12">
|
|
<div className="row">
|
|
<div className="col-sm-6 col-xs-12 checkbox">
|
|
<label id="rememberMeLabel">
|
|
<input type="checkbox" ref="rememberMe" defaultChecked={this.props.rememberMe} aria-labelledby="rememberMeLabel" />
|
|
<FormattedMessage {...loginMessages["app.login.rememberMe"]} />
|
|
</label>
|
|
</div>
|
|
<div className="col-sm-6 col-xs-12 text-right" styleName="submit">
|
|
<input type="submit" className="btn btn-default" aria-label={formatMessage(loginMessages["app.login.signIn"])} defaultValue={formatMessage(loginMessages["app.login.signIn"])} disabled={this.props.isAuthenticating} />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
LoginFormCSSIntl.propTypes = {
|
|
username: PropTypes.string,
|
|
endpoint: PropTypes.string,
|
|
rememberMe: PropTypes.bool,
|
|
onSubmit: PropTypes.func.isRequired,
|
|
isAuthenticating: PropTypes.bool,
|
|
error: PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(i18nRecord)]),
|
|
info: PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(i18nRecord)]),
|
|
intl: intlShape.isRequired,
|
|
};
|
|
export let LoginForm = injectIntl(CSSModules(LoginFormCSSIntl, css));
|
|
|
|
|
|
/**
|
|
* Main login page, including title and login form.
|
|
*/
|
|
class LoginCSS extends Component {
|
|
render() {
|
|
const greeting = (
|
|
<p>
|
|
<FormattedMessage {...loginMessages["app.login.greeting"]} />
|
|
</p>
|
|
);
|
|
return (
|
|
<div className="text-center container-fluid">
|
|
<h1><img styleName="titleImage" src="./img/ampache-blue.png" alt="A"/>mpache</h1>
|
|
<hr/>
|
|
{(!this.props.error && !this.props.info) ? greeting : null}
|
|
<div className="col-sm-9 col-sm-offset-2 col-md-6 col-md-offset-3">
|
|
<LoginForm onSubmit={this.props.onSubmit} username={this.props.username} endpoint={this.props.endpoint} rememberMe={this.props.rememberMe} isAuthenticating={this.props.isAuthenticating} error={this.props.error} info={this.props.info} />
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
LoginCSS.propTypes = {
|
|
username: PropTypes.string,
|
|
endpoint: PropTypes.string,
|
|
rememberMe: PropTypes.bool,
|
|
onSubmit: PropTypes.func.isRequired,
|
|
isAuthenticating: PropTypes.bool,
|
|
info: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
|
error: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
|
};
|
|
export default CSSModules(LoginCSS, css);
|