passbolt-styleguide
Version:
Passbolt styleguide contains common styling assets used by the different sites, plugin, etc.
232 lines (211 loc) • 7.41 kB
JavaScript
import React from "react";
import PropTypes from "prop-types";
import { Trans, withTranslation } from "react-i18next";
import SpinnerSVG from "../../../img/svg/spinner.svg";
import Password from "../../../shared/components/Password/Password";
import { withAppContext } from "../../../shared/context/AppContext/AppContext";
import CloseSVG from "../../../img/svg/close.svg";
class PassphraseDialog extends React.Component {
constructor(props) {
super(props);
this.state = this.initState();
this.initEventHandlers();
this.passphraseInputRef = React.createRef();
}
initEventHandlers() {
this.handleFormSubmit = this.handleFormSubmit.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
this.handleCloseButtonClick = this.handleCloseButtonClick.bind(this);
this.handleKeyDown = this.handleKeyDown.bind(this);
}
initState() {
return {
attempt: 0,
processing: false,
passphrase: "",
rememberMe: false,
passphraseError: "",
};
}
/**
* Whenever the component is mounted
*/
componentDidMount() {
this.focusOnPassphrase();
}
/**
* Put the focus on the passphrase input
*/
focusOnPassphrase() {
this.passphraseInputRef.current.focus();
}
/**
* Get the translate function
* @returns {function(...[*]=)}
*/
get translate() {
return this.props.t;
}
async handleFormSubmit(event) {
event.preventDefault();
this.setState({ processing: true });
if (this.state.passphrase === "") {
this.handlePassphraseError();
return;
}
try {
await this.props.context.port.request("passbolt.keyring.private.checkpassphrase", this.state.passphrase);
await this.handlePassphraseSuccess();
} catch (error) {
console.error(`Invalid passphrase":`, error);
this.handlePassphraseError();
}
}
async handlePassphraseSuccess() {
const rememberMeDuration = this.state.rememberMe ? -1 : false;
this.props.context.port.emit(this.props.requestId, "SUCCESS", {
passphrase: this.state.passphrase,
rememberMe: rememberMeDuration,
});
await this.props.onComplete();
}
handlePassphraseError() {
const isPassphraseEmpty = this.state.passphrase === "";
const errorMessage = isPassphraseEmpty
? this.translate("The passphrase should not be empty.")
: this.translate("This is not a valid passphrase.");
let attempt = this.state.attempt;
if (!isPassphraseEmpty) {
attempt++;
}
this.setState({
processing: false,
attempt: attempt,
passphraseError: errorMessage,
});
if (attempt < 3) {
// Force the passphrase input focus. The autoFocus attribute only works during the first rendering.
this.focusOnPassphrase();
}
}
handleInputChange(event) {
const target = event.target;
const value = target.type === "checkbox" ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value,
});
}
handleCloseButtonClick() {
this.props.context.port.emit(this.props.requestId, "ERROR", {
name: "UserAbortsOperationError",
message: "The dialog has been closed.",
});
this.props.onComplete();
}
handleKeyDown(event) {
// Close the dialog when the user presses the "ESC" key.
if (event.keyCode === 27) {
// If not stop it will bubble to the QuickAccess component and it will close the quickaccess dialog.
event.stopPropagation();
this.props.context.port.emit(this.props.requestId, "ERROR", {
name: "UserAbortsOperationError",
message: "The dialog has been closed.",
});
this.props.onComplete();
}
}
render() {
return (
<div className="passphrase" onKeyDown={this.handleKeyDown}>
<div className="back-link">
<a className="primary-action">
<span className="primary-action-title">
<Trans>Passphrase required</Trans>
</span>
</a>
<a
onClick={this.handleCloseButtonClick}
className="secondary-action button-transparent button"
title={this.translate("Cancel the operation")}
>
<CloseSVG />
<span className="visually-hidden">
<Trans>Cancel</Trans>
</span>
</a>
</div>
{this.state.attempt < 3 && (
<form onSubmit={this.handleFormSubmit}>
<div className="form-container">
<div className={`input-password-wrapper input required ${this.state.passphraseError ? "error" : ""}`}>
<label htmlFor="passphrase">
<Trans>Please enter your passphrase</Trans>
</label>
<Password
name="passphrase"
placeholder={this.translate("Passphrase")}
id="passphrase"
inputRef={this.passphraseInputRef}
value={this.state.passphrase}
onChange={this.handleInputChange}
disabled={this.state.processing}
securityToken={this.props.context.userSettings.getSecurityToken()}
autoComplete="off"
/>
{this.state.passphraseError && <div className="error-message">{this.state.passphraseError}</div>}
</div>
{this.props.canRememberMe && (
<div className="input checkbox">
<input
type="checkbox"
name="rememberMe"
id="remember-me"
checked={this.state.rememberMe}
onChange={this.handleInputChange}
disabled={this.state.processing}
/>
<label htmlFor="remember-me">
<Trans>Remember until I log out.</Trans>
</label>
</div>
)}
</div>
<div className="submit-wrapper">
<button
type="submit"
className={`button primary big full-width ${this.state.processing ? "processing" : ""}`}
role="button"
disabled={this.state.processing}
>
<Trans>Submit</Trans>
{this.state.processing && <SpinnerSVG />}
</button>
</div>
</form>
)}
{this.state.attempt === 3 && (
<div className="passphrase-wrong">
<div className="too-many-attempts-error">
<Trans>Your passphrase is wrong!</Trans> <Trans>The operation has been aborted.</Trans>
</div>
<div className="submit-wrapper">
<a className="button primary big full-width" role="button" onClick={this.handleCloseButtonClick}>
<Trans>Close</Trans>
</a>
</div>
</div>
)}
</div>
);
}
}
PassphraseDialog.propTypes = {
context: PropTypes.any, // The application context
canRememberMe: PropTypes.bool, // True if the remember me flag must be displayed
className: PropTypes.string,
requestId: PropTypes.string,
onComplete: PropTypes.func,
t: PropTypes.func, // The translation function
};
export default withAppContext(withTranslation("common")(PassphraseDialog));