@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/src/index.js CHANGED
@@ -1,67 +1,21 @@
1
1
  import * as React from 'react';
2
- import { generateQuery, getFrame, getMountElement } from "./utils.js";
2
+ import { hCaptchaLoader, initSentry } from '@hcaptcha/loader';
3
3
 
4
- const SCRIPT_ID = 'hcaptcha-api-script-id';
5
- const HCAPTCHA_LOAD_FN_NAME = 'hcaptchaOnLoad';
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 = frame.window.hcaptcha || undefined;
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 { isApiReady } = this.state;
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.renderCaptcha();
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? "off" : null,
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
- mountCaptchaScript(mountParams)
184
- .then(this.handleOnLoad)
185
- .catch(this.handleError);
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
- handleOnLoad () {
229
+ handleOnLoad () {
240
230
  this.setState({ isApiReady: true }, () => {
241
- const element = getMountElement(this.props.scriptLocation);
242
- const frame = getFrame(element);
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
- // render captcha and wait for captcha id
247
- this.renderCaptcha(() => {
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
- const { captchaId } = this.state;
326
- const hcaptcha = this._hcaptcha;
326
+ try {
327
+ const { captchaId } = this.state;
328
+ const hcaptcha = this._hcaptcha;
327
329
 
328
- if (!this.isReady()) {
329
- return;
330
- }
331
330
 
332
- if (opts && typeof opts !== "object") {
333
- opts = null;
334
- }
331
+ if (!this.isReady()) {
332
+ return;
333
+ }
334
+
335
+ if (opts && typeof opts !== "object") {
336
+ opts = null;
337
+ }
335
338
 
336
- return hcaptcha.execute(captchaId, opts);
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
- const doc = (element && element.ownerDocument) || document;
11
- const win = doc.defaultView || doc.parentWindow;
12
- return { document: doc, window: win };
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
- return element || document.head;
9
+ return element || document.head;
17
10
  }
18
11
 
19
12
  export {
20
- generateQuery,
21
- getFrame,
22
- getMountElement
23
- };
13
+ getFrame,
14
+ getMountElement
15
+ };
package/types/index.d.ts CHANGED
@@ -30,6 +30,8 @@ interface HCaptchaProps {
30
30
  reCaptchaCompat?: boolean;
31
31
  loadAsync?: boolean;
32
32
  scriptLocation?: HTMLElement | null;
33
+ sentry?: boolean;
34
+ cleanup?: boolean;
33
35
  }
34
36
 
35
37
  interface ExecuteResponse {