@hcaptcha/react-hcaptcha 1.8.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/constants.js +19 -0
- package/dist/esm/constants.js +11 -0
- package/dist/esm/index.js +105 -161
- package/dist/esm/utils.js +2 -18
- package/dist/index.js +104 -149
- package/dist/utils.js +1 -26
- package/package.json +3 -2
- package/src/constants.js +13 -0
- package/src/index.js +84 -78
- package/src/utils.js +8 -16
- package/types/index.d.ts +2 -0
package/src/index.js
CHANGED
|
@@ -1,67 +1,21 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { hCaptchaLoader, initSentry } from '@hcaptcha/loader';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
// Prevent loading API script multiple times
|
|
8
|
-
const scripts = [];
|
|
9
|
-
|
|
10
|
-
// Generate hCaptcha API script
|
|
11
|
-
const mountCaptchaScript = (params={}) => {
|
|
12
|
-
const element = getMountElement(params.scriptLocation);
|
|
13
|
-
delete params.scriptLocation;
|
|
14
|
-
|
|
15
|
-
const frame = getFrame(element);
|
|
16
|
-
const script = scripts.find(({ scope }) => scope === frame.window);
|
|
17
|
-
|
|
18
|
-
if (frame.document.getElementById(SCRIPT_ID) && script) {
|
|
19
|
-
// API was already requested
|
|
20
|
-
return script.promise;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const promise = new Promise((resolve, reject) => {
|
|
24
|
-
// Create global onload callback
|
|
25
|
-
frame.window[HCAPTCHA_LOAD_FN_NAME] = resolve;
|
|
26
|
-
|
|
27
|
-
const domain = params.apihost || "https://js.hcaptcha.com";
|
|
28
|
-
delete params.apihost;
|
|
29
|
-
|
|
30
|
-
const script = frame.document.createElement("script");
|
|
31
|
-
script.id = SCRIPT_ID;
|
|
32
|
-
script.src = `${domain}/1/api.js?render=explicit&onload=${HCAPTCHA_LOAD_FN_NAME}`;
|
|
33
|
-
|
|
34
|
-
script.async = params.loadAsync !== undefined? params.loadAsync : true;
|
|
35
|
-
delete params.loadAsync;
|
|
36
|
-
|
|
37
|
-
script.onerror = (event) => reject('script-error');
|
|
38
|
-
|
|
39
|
-
const query = generateQuery(params);
|
|
40
|
-
script.src += query !== ""? `&${query}` : "";
|
|
41
|
-
|
|
42
|
-
element.appendChild(script);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
scripts.push({ promise, scope: frame.window });
|
|
46
|
-
|
|
47
|
-
return promise;
|
|
48
|
-
};
|
|
4
|
+
import { getFrame, getMountElement } from './utils.js';
|
|
5
|
+
import { breadcrumbMessages, scopeTag } from "./constants";
|
|
49
6
|
|
|
50
7
|
|
|
51
8
|
class HCaptcha extends React.Component {
|
|
52
9
|
constructor (props) {
|
|
53
10
|
super(props);
|
|
54
11
|
|
|
55
|
-
const element = getMountElement(this.props.scriptLocation);
|
|
56
|
-
const frame = getFrame(element);
|
|
57
|
-
|
|
58
12
|
/**
|
|
59
13
|
* Internal reference to track hCaptcha API
|
|
60
14
|
*
|
|
61
15
|
* Required as window is relative to initialization in application
|
|
62
16
|
* not where the script and iFrames have been loaded.
|
|
63
17
|
*/
|
|
64
|
-
this._hcaptcha =
|
|
18
|
+
this._hcaptcha = undefined;
|
|
65
19
|
|
|
66
20
|
// API Methods
|
|
67
21
|
this.renderCaptcha = this.renderCaptcha.bind(this);
|
|
@@ -79,14 +33,12 @@ class HCaptcha extends React.Component {
|
|
|
79
33
|
this.handleClose = this.handleClose.bind(this);
|
|
80
34
|
this.handleChallengeExpired = this.handleChallengeExpired.bind(this);
|
|
81
35
|
|
|
82
|
-
|
|
83
|
-
const isApiReady = typeof this._hcaptcha !== 'undefined';
|
|
84
|
-
|
|
85
36
|
this.ref = React.createRef();
|
|
86
37
|
this.apiScriptRequested = false;
|
|
38
|
+
this.sentryHub = null;
|
|
87
39
|
|
|
88
40
|
this.state = {
|
|
89
|
-
isApiReady,
|
|
41
|
+
isApiReady: false,
|
|
90
42
|
isRemoved: false,
|
|
91
43
|
elementId: props.id,
|
|
92
44
|
captchaId: ''
|
|
@@ -94,7 +46,18 @@ class HCaptcha extends React.Component {
|
|
|
94
46
|
}
|
|
95
47
|
|
|
96
48
|
componentDidMount () { // Once captcha is mounted intialize hCaptcha - hCaptcha
|
|
97
|
-
const
|
|
49
|
+
const element = getMountElement(this.props.scriptLocation);
|
|
50
|
+
const frame = getFrame(element);
|
|
51
|
+
this._hcaptcha = frame.window.hcaptcha || undefined;
|
|
52
|
+
|
|
53
|
+
const isApiReady = typeof this._hcaptcha !== 'undefined';
|
|
54
|
+
|
|
55
|
+
this.sentryHub = initSentry(this.props.sentry, scopeTag);
|
|
56
|
+
|
|
57
|
+
this.sentryHub.addBreadcrumb({
|
|
58
|
+
category: scopeTag.value,
|
|
59
|
+
message: breadcrumbMessages.mounted,
|
|
60
|
+
});
|
|
98
61
|
|
|
99
62
|
/*
|
|
100
63
|
* Check if hCaptcha has already been loaded,
|
|
@@ -102,7 +65,14 @@ class HCaptcha extends React.Component {
|
|
|
102
65
|
* If No, create script tag and wait to render the captcha
|
|
103
66
|
*/
|
|
104
67
|
if (isApiReady) {
|
|
105
|
-
this.
|
|
68
|
+
this.setState(
|
|
69
|
+
{
|
|
70
|
+
isApiReady: true
|
|
71
|
+
},
|
|
72
|
+
() => {
|
|
73
|
+
this.renderCaptcha();
|
|
74
|
+
}
|
|
75
|
+
);
|
|
106
76
|
|
|
107
77
|
return;
|
|
108
78
|
}
|
|
@@ -121,6 +91,11 @@ class HCaptcha extends React.Component {
|
|
|
121
91
|
// Reset any stored variables / timers when unmounting
|
|
122
92
|
hcaptcha.reset(captchaId);
|
|
123
93
|
hcaptcha.remove(captchaId);
|
|
94
|
+
|
|
95
|
+
this.sentryHub.addBreadcrumb({
|
|
96
|
+
category: scopeTag.value,
|
|
97
|
+
message: breadcrumbMessages.unmounted,
|
|
98
|
+
});
|
|
124
99
|
}
|
|
125
100
|
|
|
126
101
|
shouldComponentUpdate(nextProps, nextState) {
|
|
@@ -163,26 +138,30 @@ class HCaptcha extends React.Component {
|
|
|
163
138
|
sentry,
|
|
164
139
|
custom,
|
|
165
140
|
loadAsync,
|
|
166
|
-
scriptLocation
|
|
141
|
+
scriptLocation,
|
|
142
|
+
cleanup = true,
|
|
167
143
|
} = this.props;
|
|
168
144
|
const mountParams = {
|
|
145
|
+
render: 'explicit',
|
|
169
146
|
apihost,
|
|
170
147
|
assethost,
|
|
171
148
|
endpoint,
|
|
172
149
|
hl,
|
|
173
150
|
host,
|
|
174
151
|
imghost,
|
|
175
|
-
recaptchacompat: reCaptchaCompat === false?
|
|
152
|
+
recaptchacompat: reCaptchaCompat === false? 'off' : null,
|
|
176
153
|
reportapi,
|
|
177
154
|
sentry,
|
|
178
155
|
custom,
|
|
179
156
|
loadAsync,
|
|
180
|
-
scriptLocation
|
|
157
|
+
scriptLocation,
|
|
158
|
+
cleanup
|
|
181
159
|
};
|
|
182
160
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
161
|
+
hCaptchaLoader(mountParams)
|
|
162
|
+
.then(this.handleOnLoad, this.handleError)
|
|
163
|
+
.catch(this.handleError);
|
|
164
|
+
|
|
186
165
|
this.apiScriptRequested = true;
|
|
187
166
|
}
|
|
188
167
|
|
|
@@ -220,6 +199,11 @@ class HCaptcha extends React.Component {
|
|
|
220
199
|
}
|
|
221
200
|
// Reset captcha state, removes stored token and unticks checkbox
|
|
222
201
|
hcaptcha.reset(captchaId)
|
|
202
|
+
|
|
203
|
+
this.sentryHub.addBreadcrumb({
|
|
204
|
+
category: scopeTag.value,
|
|
205
|
+
message: breadcrumbMessages.reset,
|
|
206
|
+
});
|
|
223
207
|
}
|
|
224
208
|
|
|
225
209
|
removeCaptcha(callback) {
|
|
@@ -234,21 +218,33 @@ class HCaptcha extends React.Component {
|
|
|
234
218
|
hcaptcha.remove(captchaId);
|
|
235
219
|
callback && callback()
|
|
236
220
|
});
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
this.sentryHub.addBreadcrumb({
|
|
224
|
+
category: scopeTag.value,
|
|
225
|
+
message: breadcrumbMessages.removed,
|
|
226
|
+
});
|
|
237
227
|
}
|
|
238
228
|
|
|
239
|
-
|
|
229
|
+
handleOnLoad () {
|
|
240
230
|
this.setState({ isApiReady: true }, () => {
|
|
241
|
-
|
|
242
|
-
|
|
231
|
+
try {
|
|
232
|
+
const element = getMountElement(this.props.scriptLocation);
|
|
233
|
+
const frame = getFrame(element);
|
|
234
|
+
|
|
235
|
+
this._hcaptcha = frame.window.hcaptcha;
|
|
243
236
|
|
|
244
|
-
this._hcaptcha = frame.window.hcaptcha;
|
|
245
237
|
|
|
246
|
-
|
|
247
|
-
|
|
238
|
+
// render captcha and wait for captcha id
|
|
239
|
+
this.renderCaptcha(() => {
|
|
248
240
|
// trigger onLoad if it exists
|
|
241
|
+
|
|
249
242
|
const { onLoad } = this.props;
|
|
250
243
|
if (onLoad) onLoad();
|
|
251
|
-
|
|
244
|
+
});
|
|
245
|
+
} catch (error) {
|
|
246
|
+
this.sentryHub.captureException(error);
|
|
247
|
+
}
|
|
252
248
|
});
|
|
253
249
|
}
|
|
254
250
|
|
|
@@ -276,6 +272,11 @@ class HCaptcha extends React.Component {
|
|
|
276
272
|
hcaptcha.reset(captchaId) // If hCaptcha runs into error, reset captcha - hCaptcha
|
|
277
273
|
|
|
278
274
|
if (onExpire) onExpire();
|
|
275
|
+
|
|
276
|
+
this.sentryHub.addBreadcrumb({
|
|
277
|
+
category: scopeTag.value,
|
|
278
|
+
message: breadcrumbMessages.expired,
|
|
279
|
+
});
|
|
279
280
|
}
|
|
280
281
|
|
|
281
282
|
handleError (event) {
|
|
@@ -322,18 +323,23 @@ class HCaptcha extends React.Component {
|
|
|
322
323
|
}
|
|
323
324
|
|
|
324
325
|
execute (opts = null) {
|
|
325
|
-
|
|
326
|
-
|
|
326
|
+
try {
|
|
327
|
+
const { captchaId } = this.state;
|
|
328
|
+
const hcaptcha = this._hcaptcha;
|
|
327
329
|
|
|
328
|
-
if (!this.isReady()) {
|
|
329
|
-
return;
|
|
330
|
-
}
|
|
331
330
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
331
|
+
if (!this.isReady()) {
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (opts && typeof opts !== "object") {
|
|
336
|
+
opts = null;
|
|
337
|
+
}
|
|
335
338
|
|
|
336
|
-
|
|
339
|
+
return hcaptcha.execute(captchaId, opts);
|
|
340
|
+
} catch (error) {
|
|
341
|
+
this.sentryHub.captureException(error);
|
|
342
|
+
}
|
|
337
343
|
}
|
|
338
344
|
|
|
339
345
|
setData (data) {
|
package/src/utils.js
CHANGED
|
@@ -1,23 +1,15 @@
|
|
|
1
|
-
function generateQuery(params) {
|
|
2
|
-
return Object.entries(params)
|
|
3
|
-
.filter(([key, value]) => value || value === false)
|
|
4
|
-
.map(([key, value]) => {
|
|
5
|
-
return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`
|
|
6
|
-
}).join("&");
|
|
7
|
-
};
|
|
8
|
-
|
|
9
1
|
function getFrame(element) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
2
|
+
const doc = (element && element.ownerDocument) || document;
|
|
3
|
+
const win = doc.defaultView || doc.parentWindow || window;
|
|
4
|
+
|
|
5
|
+
return { document: doc, window: win };
|
|
13
6
|
}
|
|
14
7
|
|
|
15
8
|
function getMountElement(element) {
|
|
16
|
-
|
|
9
|
+
return element || document.head;
|
|
17
10
|
}
|
|
18
11
|
|
|
19
12
|
export {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
};
|
|
13
|
+
getFrame,
|
|
14
|
+
getMountElement
|
|
15
|
+
};
|