Refactor CaptchaForm to put less logic in signupstages

A bunch of work being done in the Recaptcha signupstage makes more sense in
the CaptchaForm; let's move it.
This commit is contained in:
Richard van der Hoff 2016-10-11 18:00:47 +01:00
parent 6ffddabaaa
commit eb36509620
3 changed files with 77 additions and 68 deletions

View file

@ -53,66 +53,24 @@ class RecaptchaStage extends Stage {
constructor(matrixClient, signupInstance) { constructor(matrixClient, signupInstance) {
super(RecaptchaStage.TYPE, matrixClient, signupInstance); super(RecaptchaStage.TYPE, matrixClient, signupInstance);
this.defer = q.defer(); // resolved with the captcha response this.defer = q.defer(); // resolved with the captcha response
this.publicKey = null; // from the HS
this.divId = null; // from the UI component
} }
// called when the UI component has loaded the recaptcha <div> so we can // called when the recaptcha has been completed.
// render to it.
onReceiveData(data) { onReceiveData(data) {
if (!data || !data.divId) { if (!data || !data.response) {
return; return;
} }
this.divId = data.divId; this.defer.resolve({
this._attemptRender(); auth: {
type: 'm.login.recaptcha',
response: data.response,
}
});
} }
complete() { complete() {
var publicKey;
var serverParams = this.signupInstance.getServerData().params;
if (serverParams && serverParams["m.login.recaptcha"]) {
publicKey = serverParams["m.login.recaptcha"].public_key;
}
if (!publicKey) {
return q.reject({
message: "This server has not supplied enough information for Recaptcha " +
"authentication",
isFatal: true
});
}
this.publicKey = publicKey;
this._attemptRender();
return this.defer.promise; return this.defer.promise;
} }
_attemptRender() {
if (!global.grecaptcha) {
console.error("grecaptcha not loaded!");
return;
}
if (!this.publicKey) {
console.error("No public key for recaptcha!");
return;
}
if (!this.divId) {
console.error("No div ID specified!");
return;
}
console.log("Rendering to %s", this.divId);
var self = this;
global.grecaptcha.render(this.divId, {
sitekey: this.publicKey,
callback: function(response) {
console.log("Received captcha response");
self.defer.resolve({
auth: {
type: 'm.login.recaptcha',
response: response
}
});
}
});
}
} }
RecaptchaStage.TYPE = "m.login.recaptcha"; RecaptchaStage.TYPE = "m.login.recaptcha";

View file

@ -228,12 +228,9 @@ module.exports = React.createClass({
}); });
}, },
onCaptchaLoaded: function(divIdName) { onCaptchaResponse: function(response) {
this.registerLogic.tellStage("m.login.recaptcha", { this.registerLogic.tellStage("m.login.recaptcha", {
divId: divIdName response: response
});
this.setState({
busy: false // requires user input
}); });
}, },
@ -267,8 +264,15 @@ module.exports = React.createClass({
); );
break; break;
case "Register.STEP_m.login.recaptcha": case "Register.STEP_m.login.recaptcha":
var publicKey;
var serverParams = this.registerLogic.getServerData().params;
if (serverParams && serverParams["m.login.recaptcha"]) {
publicKey = serverParams["m.login.recaptcha"].public_key;
}
registerStep = ( registerStep = (
<CaptchaForm onCaptchaLoaded={this.onCaptchaLoaded} /> <CaptchaForm sitePublicKey={publicKey}
onCaptchaResponse={this.onCaptchaResponse}
/>
); );
break; break;
default: default:

View file

@ -26,28 +26,34 @@ module.exports = React.createClass({
displayName: 'CaptchaForm', displayName: 'CaptchaForm',
propTypes: { propTypes: {
onCaptchaLoaded: React.PropTypes.func.isRequired // called with div id name sitePublicKey: React.PropTypes.string,
// called with the captcha response
onCaptchaResponse: React.PropTypes.func,
}, },
getDefaultProps: function() { getDefaultProps: function() {
return { return {
onCaptchaLoaded: function() { onCaptchaResponse: () => {},
console.error("Unhandled onCaptchaLoaded"); };
} },
getInitialState: function() {
return {
errorText: null,
}; };
}, },
componentDidMount: function() { componentDidMount: function() {
// Just putting a script tag into the returned jsx doesn't work, annoyingly, // Just putting a script tag into the returned jsx doesn't work, annoyingly,
// so we do this instead. // so we do this instead.
var self = this; if (global.grecaptcha) {
if (this.refs.recaptchaContainer) { // already loaded
this._onCaptchaLoaded();
} else {
console.log("Loading recaptcha script..."); console.log("Loading recaptcha script...");
var scriptTag = document.createElement('script'); var scriptTag = document.createElement('script');
window.mx_on_recaptcha_loaded = function() { window.mx_on_recaptcha_loaded = () => {this._onCaptchaLoaded()};
console.log("Loaded recaptcha script.");
self.props.onCaptchaLoaded(DIV_ID);
};
scriptTag.setAttribute( scriptTag.setAttribute(
'src', global.location.protocol+"//www.google.com/recaptcha/api.js?onload=mx_on_recaptcha_loaded&render=explicit" 'src', global.location.protocol+"//www.google.com/recaptcha/api.js?onload=mx_on_recaptcha_loaded&render=explicit"
); );
@ -55,13 +61,54 @@ module.exports = React.createClass({
} }
}, },
_renderRecaptcha: function(divId) {
if (!global.grecaptcha) {
console.error("grecaptcha not loaded!");
throw new Error("Recaptcha did not load successfully");
}
var publicKey = this.props.sitePublicKey;
if (!publicKey) {
console.error("No public key for recaptcha!");
throw new Error(
"This server has not supplied enough information for Recaptcha "
+ "authentication");
}
console.log("Rendering to %s", divId);
global.grecaptcha.render(divId, {
sitekey: publicKey,
callback: this.props.onCaptchaResponse,
});
},
_onCaptchaLoaded: function() {
console.log("Loaded recaptcha script.");
try {
this._renderRecaptcha(DIV_ID);
} catch (e) {
this.setState({
errorText: e.toString(),
})
}
},
render: function() { render: function() {
// FIXME: Tight coupling with the div id and SignupStages.js let error = null;
if (this.state.errorText) {
error = (
<div className="error">
{this.state.errorText}
</div>
);
}
return ( return (
<div ref="recaptchaContainer"> <div ref="recaptchaContainer">
This Home Server would like to make sure you are not a robot This Home Server would like to make sure you are not a robot
<div id={DIV_ID}></div> <div id={DIV_ID}></div>
{error}
</div> </div>
); );
} }
}); });