@atlaskit/smart-card 23.11.2 → 23.12.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/cjs/messages.js +5 -0
  3. package/dist/cjs/state/analytics/useSmartLinkAnalytics.js +216 -146
  4. package/dist/cjs/utils/analytics/analytics.js +134 -74
  5. package/dist/cjs/utils/analytics/index.js +6 -0
  6. package/dist/cjs/version.json +1 -1
  7. package/dist/cjs/view/CardWithUrl/component.js +27 -1
  8. package/dist/cjs/view/EmbedCard/components/Frame.js +110 -5
  9. package/dist/cjs/view/EmbedCard/components/IframeDwellTracker.js +75 -0
  10. package/dist/cjs/view/EmbedCard/index.js +6 -2
  11. package/dist/cjs/view/EmbedCard/views/ResolvedView.js +6 -2
  12. package/dist/cjs/view/HoverCard/components/HoverCardContent.js +49 -2
  13. package/dist/es2019/messages.js +5 -0
  14. package/dist/es2019/state/analytics/useSmartLinkAnalytics.js +69 -1
  15. package/dist/es2019/utils/analytics/analytics.js +52 -0
  16. package/dist/es2019/utils/analytics/index.js +1 -1
  17. package/dist/es2019/version.json +1 -1
  18. package/dist/es2019/view/CardWithUrl/component.js +27 -1
  19. package/dist/es2019/view/EmbedCard/components/Frame.js +91 -6
  20. package/dist/es2019/view/EmbedCard/components/IframeDwellTracker.js +57 -0
  21. package/dist/es2019/view/EmbedCard/index.js +6 -2
  22. package/dist/es2019/view/EmbedCard/views/ResolvedView.js +6 -2
  23. package/dist/es2019/view/HoverCard/components/HoverCardContent.js +13 -1
  24. package/dist/esm/messages.js +5 -0
  25. package/dist/esm/state/analytics/useSmartLinkAnalytics.js +217 -147
  26. package/dist/esm/utils/analytics/analytics.js +127 -73
  27. package/dist/esm/utils/analytics/index.js +1 -1
  28. package/dist/esm/version.json +1 -1
  29. package/dist/esm/view/CardWithUrl/component.js +27 -1
  30. package/dist/esm/view/EmbedCard/components/Frame.js +110 -6
  31. package/dist/esm/view/EmbedCard/components/IframeDwellTracker.js +62 -0
  32. package/dist/esm/view/EmbedCard/index.js +6 -2
  33. package/dist/esm/view/EmbedCard/views/ResolvedView.js +6 -2
  34. package/dist/esm/view/HoverCard/components/HoverCardContent.js +43 -1
  35. package/dist/types/messages.d.ts +1 -1
  36. package/dist/types/state/analytics/useSmartLinkAnalytics.d.ts +27 -1
  37. package/dist/types/state/flexible-ui-context/index.d.ts +4 -0
  38. package/dist/types/state/hooks/useSmartLink.d.ts +2 -0
  39. package/dist/types/utils/analytics/analytics.d.ts +3 -1
  40. package/dist/types/utils/analytics/index.d.ts +1 -1
  41. package/dist/types/utils/analytics/types.d.ts +12 -0
  42. package/dist/types/utils/mocks.d.ts +2 -0
  43. package/dist/types/utils/types.d.ts +2 -2
  44. package/dist/types/view/EmbedCard/components/Frame.d.ts +2 -0
  45. package/dist/types/view/EmbedCard/components/IframeDwellTracker.d.ts +8 -0
  46. package/dist/types/view/EmbedCard/types.d.ts +2 -0
  47. package/dist/types/view/EmbedCard/views/ResolvedView.d.ts +4 -0
  48. package/dist/types/view/HoverCard/components/HoverCardContent.d.ts +1 -0
  49. package/package.json +2 -2
  50. package/report.api.md +42 -1
  51. package/tmp/api-report-tmp.d.ts +20 -2
@@ -315,15 +315,69 @@ export var uiCardClickedEvent = function uiCardClickedEvent(_ref11) {
315
315
  })
316
316
  };
317
317
  };
318
- export var uiActionClickedEvent = function uiActionClickedEvent(_ref12) {
318
+ export var uiIframeDwelledEvent = function uiIframeDwelledEvent(_ref12) {
319
319
  var id = _ref12.id,
320
- actionType = _ref12.actionType,
321
- extensionKey = _ref12.extensionKey,
322
320
  display = _ref12.display,
321
+ status = _ref12.status,
323
322
  definitionId = _ref12.definitionId,
323
+ extensionKey = _ref12.extensionKey,
324
+ location = _ref12.location,
324
325
  destinationProduct = _ref12.destinationProduct,
325
326
  destinationSubproduct = _ref12.destinationSubproduct,
326
- location = _ref12.location;
327
+ dwellTime = _ref12.dwellTime,
328
+ dwellPercentVisible = _ref12.dwellPercentVisible;
329
+ return {
330
+ action: 'dwelled',
331
+ actionSubject: 'smartLinkIframe',
332
+ eventType: 'ui',
333
+ attributes: _objectSpread(_objectSpread({}, context), {}, {
334
+ id: id,
335
+ status: status,
336
+ definitionId: definitionId,
337
+ extensionKey: extensionKey,
338
+ display: display,
339
+ location: location,
340
+ destinationProduct: destinationProduct,
341
+ destinationSubproduct: destinationSubproduct,
342
+ dwellTime: dwellTime,
343
+ dwellPercentVisible: dwellPercentVisible
344
+ })
345
+ };
346
+ };
347
+ export var uiIframeFocusedEvent = function uiIframeFocusedEvent(_ref13) {
348
+ var id = _ref13.id,
349
+ display = _ref13.display,
350
+ status = _ref13.status,
351
+ definitionId = _ref13.definitionId,
352
+ extensionKey = _ref13.extensionKey,
353
+ location = _ref13.location,
354
+ destinationProduct = _ref13.destinationProduct,
355
+ destinationSubproduct = _ref13.destinationSubproduct;
356
+ return {
357
+ action: 'focused',
358
+ actionSubject: 'smartLinkIframe',
359
+ eventType: 'ui',
360
+ attributes: _objectSpread(_objectSpread({}, context), {}, {
361
+ id: id,
362
+ status: status,
363
+ definitionId: definitionId,
364
+ extensionKey: extensionKey,
365
+ display: display,
366
+ location: location,
367
+ destinationProduct: destinationProduct,
368
+ destinationSubproduct: destinationSubproduct
369
+ })
370
+ };
371
+ };
372
+ export var uiActionClickedEvent = function uiActionClickedEvent(_ref14) {
373
+ var id = _ref14.id,
374
+ actionType = _ref14.actionType,
375
+ extensionKey = _ref14.extensionKey,
376
+ display = _ref14.display,
377
+ definitionId = _ref14.definitionId,
378
+ destinationProduct = _ref14.destinationProduct,
379
+ destinationSubproduct = _ref14.destinationSubproduct,
380
+ location = _ref14.location;
327
381
  return {
328
382
  action: 'clicked',
329
383
  actionSubject: 'button',
@@ -341,13 +395,13 @@ export var uiActionClickedEvent = function uiActionClickedEvent(_ref12) {
341
395
  })
342
396
  };
343
397
  };
344
- export var uiClosedAuthEvent = function uiClosedAuthEvent(_ref13) {
345
- var display = _ref13.display,
346
- extensionKey = _ref13.extensionKey,
347
- definitionId = _ref13.definitionId,
348
- destinationProduct = _ref13.destinationProduct,
349
- destinationSubproduct = _ref13.destinationSubproduct,
350
- location = _ref13.location;
398
+ export var uiClosedAuthEvent = function uiClosedAuthEvent(_ref15) {
399
+ var display = _ref15.display,
400
+ extensionKey = _ref15.extensionKey,
401
+ definitionId = _ref15.definitionId,
402
+ destinationProduct = _ref15.destinationProduct,
403
+ destinationSubproduct = _ref15.destinationSubproduct,
404
+ location = _ref15.location;
351
405
  return {
352
406
  action: 'closed',
353
407
  actionSubject: 'consentModal',
@@ -362,12 +416,12 @@ export var uiClosedAuthEvent = function uiClosedAuthEvent(_ref13) {
362
416
  })
363
417
  };
364
418
  };
365
- export var screenAuthPopupEvent = function screenAuthPopupEvent(_ref14) {
366
- var extensionKey = _ref14.extensionKey,
367
- definitionId = _ref14.definitionId,
368
- destinationProduct = _ref14.destinationProduct,
369
- destinationSubproduct = _ref14.destinationSubproduct,
370
- location = _ref14.location;
419
+ export var screenAuthPopupEvent = function screenAuthPopupEvent(_ref16) {
420
+ var extensionKey = _ref16.extensionKey,
421
+ definitionId = _ref16.definitionId,
422
+ destinationProduct = _ref16.destinationProduct,
423
+ destinationSubproduct = _ref16.destinationSubproduct,
424
+ location = _ref16.location;
371
425
  return {
372
426
  actionSubject: 'consentModal',
373
427
  eventType: 'screen',
@@ -380,14 +434,14 @@ export var screenAuthPopupEvent = function screenAuthPopupEvent(_ref14) {
380
434
  })
381
435
  };
382
436
  };
383
- export var uiRenderSuccessEvent = function uiRenderSuccessEvent(_ref15) {
384
- var display = _ref15.display,
385
- status = _ref15.status,
386
- extensionKey = _ref15.extensionKey,
387
- definitionId = _ref15.definitionId,
388
- destinationProduct = _ref15.destinationProduct,
389
- destinationSubproduct = _ref15.destinationSubproduct,
390
- location = _ref15.location;
437
+ export var uiRenderSuccessEvent = function uiRenderSuccessEvent(_ref17) {
438
+ var display = _ref17.display,
439
+ status = _ref17.status,
440
+ extensionKey = _ref17.extensionKey,
441
+ definitionId = _ref17.definitionId,
442
+ destinationProduct = _ref17.destinationProduct,
443
+ destinationSubproduct = _ref17.destinationSubproduct,
444
+ location = _ref17.location;
391
445
  return {
392
446
  action: 'renderSuccess',
393
447
  actionSubject: 'smartLink',
@@ -403,15 +457,15 @@ export var uiRenderSuccessEvent = function uiRenderSuccessEvent(_ref15) {
403
457
  })
404
458
  };
405
459
  };
406
- export var uiRenderFailedEvent = function uiRenderFailedEvent(_ref16) {
407
- var display = _ref16.display,
408
- error = _ref16.error,
409
- errorInfo = _ref16.errorInfo,
410
- extensionKey = _ref16.extensionKey,
411
- definitionId = _ref16.definitionId,
412
- destinationProduct = _ref16.destinationProduct,
413
- destinationSubproduct = _ref16.destinationSubproduct,
414
- location = _ref16.location;
460
+ export var uiRenderFailedEvent = function uiRenderFailedEvent(_ref18) {
461
+ var display = _ref18.display,
462
+ error = _ref18.error,
463
+ errorInfo = _ref18.errorInfo,
464
+ extensionKey = _ref18.extensionKey,
465
+ definitionId = _ref18.definitionId,
466
+ destinationProduct = _ref18.destinationProduct,
467
+ destinationSubproduct = _ref18.destinationSubproduct,
468
+ location = _ref18.location;
415
469
  return {
416
470
  actionSubject: 'smartLink',
417
471
  action: 'renderFailed',
@@ -428,16 +482,16 @@ export var uiRenderFailedEvent = function uiRenderFailedEvent(_ref16) {
428
482
  })
429
483
  };
430
484
  };
431
- export var uiHoverCardViewedEvent = function uiHoverCardViewedEvent(_ref17) {
432
- var id = _ref17.id,
433
- previewDisplay = _ref17.previewDisplay,
434
- extensionKey = _ref17.extensionKey,
435
- definitionId = _ref17.definitionId,
436
- destinationProduct = _ref17.destinationProduct,
437
- destinationSubproduct = _ref17.destinationSubproduct,
438
- location = _ref17.location,
439
- previewInvokeMethod = _ref17.previewInvokeMethod,
440
- status = _ref17.status;
485
+ export var uiHoverCardViewedEvent = function uiHoverCardViewedEvent(_ref19) {
486
+ var id = _ref19.id,
487
+ previewDisplay = _ref19.previewDisplay,
488
+ extensionKey = _ref19.extensionKey,
489
+ definitionId = _ref19.definitionId,
490
+ destinationProduct = _ref19.destinationProduct,
491
+ destinationSubproduct = _ref19.destinationSubproduct,
492
+ location = _ref19.location,
493
+ previewInvokeMethod = _ref19.previewInvokeMethod,
494
+ status = _ref19.status;
441
495
  return {
442
496
  action: 'viewed',
443
497
  actionSubject: 'hoverCard',
@@ -455,17 +509,17 @@ export var uiHoverCardViewedEvent = function uiHoverCardViewedEvent(_ref17) {
455
509
  })
456
510
  };
457
511
  };
458
- export var uiHoverCardDismissedEvent = function uiHoverCardDismissedEvent(_ref18) {
459
- var id = _ref18.id,
460
- previewDisplay = _ref18.previewDisplay,
461
- hoverTime = _ref18.hoverTime,
462
- extensionKey = _ref18.extensionKey,
463
- definitionId = _ref18.definitionId,
464
- destinationProduct = _ref18.destinationProduct,
465
- destinationSubproduct = _ref18.destinationSubproduct,
466
- location = _ref18.location,
467
- previewInvokeMethod = _ref18.previewInvokeMethod,
468
- status = _ref18.status;
512
+ export var uiHoverCardDismissedEvent = function uiHoverCardDismissedEvent(_ref20) {
513
+ var id = _ref20.id,
514
+ previewDisplay = _ref20.previewDisplay,
515
+ hoverTime = _ref20.hoverTime,
516
+ extensionKey = _ref20.extensionKey,
517
+ definitionId = _ref20.definitionId,
518
+ destinationProduct = _ref20.destinationProduct,
519
+ destinationSubproduct = _ref20.destinationSubproduct,
520
+ location = _ref20.location,
521
+ previewInvokeMethod = _ref20.previewInvokeMethod,
522
+ status = _ref20.status;
469
523
  return {
470
524
  action: 'dismissed',
471
525
  actionSubject: 'hoverCard',
@@ -484,15 +538,15 @@ export var uiHoverCardDismissedEvent = function uiHoverCardDismissedEvent(_ref18
484
538
  })
485
539
  };
486
540
  };
487
- export var uiHoverCardOpenLinkClickedEvent = function uiHoverCardOpenLinkClickedEvent(_ref19) {
488
- var id = _ref19.id,
489
- previewDisplay = _ref19.previewDisplay,
490
- extensionKey = _ref19.extensionKey,
491
- definitionId = _ref19.definitionId,
492
- destinationProduct = _ref19.destinationProduct,
493
- destinationSubproduct = _ref19.destinationSubproduct,
494
- location = _ref19.location,
495
- previewInvokeMethod = _ref19.previewInvokeMethod;
541
+ export var uiHoverCardOpenLinkClickedEvent = function uiHoverCardOpenLinkClickedEvent(_ref21) {
542
+ var id = _ref21.id,
543
+ previewDisplay = _ref21.previewDisplay,
544
+ extensionKey = _ref21.extensionKey,
545
+ definitionId = _ref21.definitionId,
546
+ destinationProduct = _ref21.destinationProduct,
547
+ destinationSubproduct = _ref21.destinationSubproduct,
548
+ location = _ref21.location,
549
+ previewInvokeMethod = _ref21.previewInvokeMethod;
496
550
  return {
497
551
  action: 'clicked',
498
552
  actionSubject: 'button',
@@ -519,15 +573,15 @@ export var uiLearnMoreLinkClickedEvent = function uiLearnMoreLinkClickedEvent()
519
573
  attributes: _objectSpread({}, context)
520
574
  };
521
575
  };
522
- export var chunkloadFailedEvent = function chunkloadFailedEvent(_ref20) {
523
- var display = _ref20.display,
524
- error = _ref20.error,
525
- errorInfo = _ref20.errorInfo,
526
- extensionKey = _ref20.extensionKey,
527
- definitionId = _ref20.definitionId,
528
- destinationProduct = _ref20.destinationProduct,
529
- destinationSubproduct = _ref20.destinationSubproduct,
530
- location = _ref20.location;
576
+ export var chunkloadFailedEvent = function chunkloadFailedEvent(_ref22) {
577
+ var display = _ref22.display,
578
+ error = _ref22.error,
579
+ errorInfo = _ref22.errorInfo,
580
+ extensionKey = _ref22.extensionKey,
581
+ definitionId = _ref22.definitionId,
582
+ destinationProduct = _ref22.destinationProduct,
583
+ destinationSubproduct = _ref22.destinationSubproduct,
584
+ location = _ref22.location;
531
585
  return {
532
586
  action: 'chunkLoadFailed',
533
587
  actionSubject: 'smartLink',
@@ -6,7 +6,7 @@ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { va
6
6
 
7
7
  import { getMeasure } from '../performance';
8
8
  import { resolvedEvent, unresolvedEvent } from './analytics';
9
- export { ANALYTICS_CHANNEL, context, fireSmartLinkEvent, resolvedEvent, unresolvedEvent, invokeSucceededEvent, invokeFailedEvent, chunkloadFailedEvent, connectSucceededEvent, connectFailedEvent, trackAppAccountAuthStarted, trackAppAccountConnected, uiAuthEvent, uiAuthAlternateAccountEvent, uiCardClickedEvent, uiActionClickedEvent, uiClosedAuthEvent, screenAuthPopupEvent, uiRenderSuccessEvent, uiRenderFailedEvent, uiHoverCardViewedEvent, uiHoverCardDismissedEvent, uiHoverCardOpenLinkClickedEvent, uiLearnMoreLinkClickedEvent } from './analytics';
9
+ export { ANALYTICS_CHANNEL, context, fireSmartLinkEvent, resolvedEvent, unresolvedEvent, invokeSucceededEvent, invokeFailedEvent, chunkloadFailedEvent, connectSucceededEvent, connectFailedEvent, trackAppAccountAuthStarted, trackAppAccountConnected, uiAuthEvent, uiAuthAlternateAccountEvent, uiCardClickedEvent, uiActionClickedEvent, uiClosedAuthEvent, screenAuthPopupEvent, uiRenderSuccessEvent, uiRenderFailedEvent, uiHoverCardViewedEvent, uiHoverCardDismissedEvent, uiHoverCardOpenLinkClickedEvent, uiLearnMoreLinkClickedEvent, uiIframeDwelledEvent } from './analytics';
10
10
  export var instrumentEvent = function instrumentEvent(_ref) {
11
11
  var id = _ref.id,
12
12
  status = _ref.status,
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/smart-card",
3
- "version": "23.11.2",
3
+ "version": "23.12.0",
4
4
  "sideEffects": false
5
5
  }
@@ -119,6 +119,30 @@ export function CardWithUrlContent(_ref) {
119
119
  });
120
120
  }
121
121
  }, [appearance, state.status, url, definitionId, extensionKey, analytics.ui, id]);
122
+ var onIframeDwell = useCallback(function (dwellTime, dwellPercentVisible) {
123
+ analytics.ui.iframeDwelledEvent({
124
+ id: id,
125
+ display: isFlexibleUi ? 'flexible' : appearance,
126
+ status: state.status,
127
+ definitionId: definitionId,
128
+ extensionKey: extensionKey,
129
+ destinationProduct: product,
130
+ destinationSubproduct: subproduct,
131
+ dwellTime: dwellTime,
132
+ dwellPercentVisible: dwellPercentVisible
133
+ });
134
+ }, [id, state.status, analytics.ui, appearance, definitionId, extensionKey, isFlexibleUi, product, subproduct]);
135
+ var onIframeFocus = useCallback(function () {
136
+ analytics.ui.iframeFocusedEvent({
137
+ id: id,
138
+ display: isFlexibleUi ? 'flexible' : appearance,
139
+ status: state.status,
140
+ definitionId: definitionId,
141
+ extensionKey: extensionKey,
142
+ destinationProduct: product,
143
+ destinationSubproduct: subproduct
144
+ });
145
+ }, [id, state.status, analytics.ui, appearance, definitionId, extensionKey, isFlexibleUi, product, subproduct]);
122
146
 
123
147
  if (isFlexibleUi) {
124
148
  var cardState = state;
@@ -210,7 +234,9 @@ export function CardWithUrlContent(_ref) {
210
234
  testId: testId,
211
235
  inheritDimensions: inheritDimensions,
212
236
  showActions: showActions,
213
- ref: embedIframeRef
237
+ ref: embedIframeRef,
238
+ onIframeDwell: onIframeDwell,
239
+ onIframeFocus: onIframeFocus
214
240
  });
215
241
  }
216
242
  }
@@ -2,25 +2,123 @@ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
2
 
3
3
  /** @jsx jsx */
4
4
  import { jsx } from '@emotion/react';
5
- import React, { useState } from 'react';
5
+ import React, { useEffect, useState, useRef } from 'react';
6
6
  import { getIframeSandboxAttribute } from '../../../utils';
7
+ import { IframeDwellTracker } from './IframeDwellTracker';
8
+ import { useFeatureFlag } from '@atlaskit/link-provider';
9
+
10
+ function mergeRefs(refs) {
11
+ return function (value) {
12
+ refs.forEach(function (ref) {
13
+ if (typeof ref === 'function') {
14
+ ref(value);
15
+ } else if (ref !== null) {
16
+ ref.current = value;
17
+ }
18
+ });
19
+ };
20
+ }
21
+
7
22
  export var Frame = /*#__PURE__*/React.forwardRef(function (_ref, iframeRef) {
8
23
  var url = _ref.url,
9
24
  _ref$isTrusted = _ref.isTrusted,
10
25
  isTrusted = _ref$isTrusted === void 0 ? false : _ref$isTrusted,
11
- testId = _ref.testId;
26
+ testId = _ref.testId,
27
+ onIframeDwell = _ref.onIframeDwell,
28
+ onIframeFocus = _ref.onIframeFocus;
12
29
 
13
30
  var _useState = useState(false),
14
31
  _useState2 = _slicedToArray(_useState, 2),
15
32
  isIframeLoaded = _useState2[0],
16
33
  setIframeLoaded = _useState2[1];
17
34
 
35
+ var _useState3 = useState(false),
36
+ _useState4 = _slicedToArray(_useState3, 2),
37
+ isMouseOver = _useState4[0],
38
+ setMouseOver = _useState4[1];
39
+
40
+ var _useState5 = useState(true),
41
+ _useState6 = _slicedToArray(_useState5, 2),
42
+ isWindowFocused = _useState6[0],
43
+ setWindowFocused = _useState6[1];
44
+
45
+ var ref = useRef();
46
+ var mergedRef = mergeRefs([iframeRef, ref]);
47
+ var trackIframeDwellEvents = useFeatureFlag('trackIframeDwellEvents');
48
+
49
+ var _useState7 = useState(0),
50
+ _useState8 = _slicedToArray(_useState7, 2),
51
+ percentVisible = _useState8[0],
52
+ setPercentVisible = _useState8[1];
53
+ /**
54
+ * These are the 'percent visible' thresholds at which the intersectionObserver will
55
+ * trigger a state change. Eg. when the user scrolls and moves from 74% to 76%, or
56
+ * vice versa. It's in a state object so that its static for the useEffect
57
+ */
58
+
59
+
60
+ var _useState9 = useState([0.75, 0.8, 0.85, 0.9, 0.95, 1]),
61
+ _useState10 = _slicedToArray(_useState9, 1),
62
+ threshold = _useState10[0];
63
+
64
+ useEffect(function () {
65
+ if (!trackIframeDwellEvents) {
66
+ return;
67
+ }
68
+
69
+ if (!ref || !ref.current) {
70
+ return;
71
+ }
72
+
73
+ var observer = new IntersectionObserver(function (entries) {
74
+ entries.forEach(function (entry) {
75
+ setPercentVisible(entry === null || entry === void 0 ? void 0 : entry.intersectionRatio);
76
+ });
77
+ }, {
78
+ threshold: threshold
79
+ });
80
+ observer.observe(ref.current);
81
+ return function () {
82
+ observer.disconnect();
83
+ };
84
+ }, [trackIframeDwellEvents, threshold, mergedRef]);
85
+ useEffect(function () {
86
+ if (!trackIframeDwellEvents) {
87
+ return;
88
+ }
89
+
90
+ var onBlur = function onBlur() {
91
+ setWindowFocused(false);
92
+
93
+ if (document.activeElement === ref.current) {
94
+ onIframeFocus && onIframeFocus();
95
+ }
96
+ };
97
+
98
+ var onFocus = function onFocus() {
99
+ setWindowFocused(true);
100
+ };
101
+
102
+ window.addEventListener('blur', onBlur);
103
+ window.addEventListener('focus', onFocus);
104
+ return function () {
105
+ window.removeEventListener('blur', onBlur);
106
+ window.removeEventListener('focus', onFocus);
107
+ };
108
+ }, [trackIframeDwellEvents, ref, onIframeFocus]);
109
+
18
110
  if (!url) {
19
111
  return null;
20
112
  }
21
113
 
22
- return jsx("iframe", {
23
- ref: iframeRef,
114
+ return jsx(React.Fragment, null, trackIframeDwellEvents ? jsx(IframeDwellTracker, {
115
+ isIframeLoaded: isIframeLoaded,
116
+ isMouseOver: isMouseOver,
117
+ isWindowFocused: isWindowFocused,
118
+ iframePercentVisible: percentVisible,
119
+ onIframeDwell: onIframeDwell
120
+ }) : null, jsx("iframe", {
121
+ ref: mergedRef,
24
122
  src: url,
25
123
  "data-testid": "".concat(testId, "-frame"),
26
124
  "data-iframe-loaded": isIframeLoaded,
@@ -34,12 +132,18 @@ export var Frame = /*#__PURE__*/React.forwardRef(function (_ref, iframeRef) {
34
132
  overflow: 'hidden',
35
133
  borderRadius: '3px'
36
134
  },
135
+ onMouseEnter: trackIframeDwellEvents ? function () {
136
+ return setMouseOver(true);
137
+ } : undefined,
138
+ onMouseLeave: trackIframeDwellEvents ? function () {
139
+ return setMouseOver(false);
140
+ } : undefined,
37
141
  allowFullScreen: true,
38
142
  scrolling: "yes",
39
143
  allow: "autoplay; encrypted-media; clipboard-write",
40
144
  onLoad: function onLoad() {
41
- return setIframeLoaded(true);
145
+ setIframeLoaded(true);
42
146
  },
43
147
  sandbox: getIframeSandboxAttribute(isTrusted)
44
- });
148
+ }));
45
149
  });
@@ -0,0 +1,62 @@
1
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
+
3
+ /** @jsx jsx */
4
+ import { useEffect, useRef, useState } from 'react';
5
+
6
+ /**
7
+ * A kind of cheap logarithmic backoff. Fire analytics after the user has
8
+ * dwelled for 5 seconds, then 10 seconds, and so on.
9
+ */
10
+ var INTERVALS_TO_LOG = [5, 10, 15, 20, 25, 30, 45, 60, 90, 120, 180];
11
+ export var IframeDwellTracker = function IframeDwellTracker(_ref) {
12
+ var isIframeLoaded = _ref.isIframeLoaded,
13
+ isMouseOver = _ref.isMouseOver,
14
+ isWindowFocused = _ref.isWindowFocused,
15
+ iframePercentVisible = _ref.iframePercentVisible,
16
+ onIframeDwell = _ref.onIframeDwell;
17
+
18
+ var _useState = useState({
19
+ time: 0,
20
+ percentVisible: 0
21
+ }),
22
+ _useState2 = _slicedToArray(_useState, 2),
23
+ dwell = _useState2[0],
24
+ setDwell = _useState2[1];
25
+
26
+ var dwellTimeoutId = useRef(); // fire analytics when the dwell timer reaches 5 seconds
27
+
28
+ useEffect(function () {
29
+ if (dwell.time > 0 && INTERVALS_TO_LOG.includes(dwell.time)) {
30
+ onIframeDwell && onIframeDwell(dwell.time, dwell.percentVisible);
31
+ }
32
+ }, [dwell, onIframeDwell]); // start and stop tracking dwell time
33
+
34
+ useEffect(function () {
35
+ var incrementDwellTime = function incrementDwellTime() {
36
+ // callback is called on dwell state changes, so change percentVisible and time together
37
+ setDwell(function (_ref2) {
38
+ var time = _ref2.time;
39
+ return {
40
+ time: time + 1,
41
+ // snapshot of percent visible, rounded down to nearest 5%
42
+ percentVisible: Math.floor((iframePercentVisible || 0) * 20) * 5
43
+ };
44
+ });
45
+ };
46
+
47
+ if (isIframeLoaded && isMouseOver && isWindowFocused && iframePercentVisible > 0.75) {
48
+ if (dwellTimeoutId.current) {
49
+ clearInterval(dwellTimeoutId.current);
50
+ }
51
+
52
+ dwellTimeoutId.current = setInterval(incrementDwellTime, 1000);
53
+ }
54
+
55
+ return function () {
56
+ if (dwellTimeoutId.current) {
57
+ clearInterval(dwellTimeoutId.current);
58
+ }
59
+ };
60
+ }, [setDwell, isMouseOver, isWindowFocused, isIframeLoaded, iframePercentVisible]);
61
+ return null;
62
+ };
@@ -32,7 +32,9 @@ export var EmbedCard = /*#__PURE__*/React.forwardRef(function (_ref, iframeRef)
32
32
  onResolve = _ref.onResolve,
33
33
  onError = _ref.onError,
34
34
  testId = _ref.testId,
35
- inheritDimensions = _ref.inheritDimensions;
35
+ inheritDimensions = _ref.inheritDimensions,
36
+ onIframeDwell = _ref.onIframeDwell,
37
+ onIframeFocus = _ref.onIframeFocus;
36
38
  var data = details && details.data || getEmptyJsonLd();
37
39
  var meta = details && details.meta;
38
40
  var extensionKey = getExtensionKey(details);
@@ -65,7 +67,9 @@ export var EmbedCard = /*#__PURE__*/React.forwardRef(function (_ref, iframeRef)
65
67
  isFrameVisible: isFrameVisible,
66
68
  inheritDimensions: inheritDimensions,
67
69
  onClick: handleFrameClick,
68
- ref: iframeRef
70
+ ref: iframeRef,
71
+ onIframeDwell: onIframeDwell,
72
+ onIframeFocus: onIframeFocus
69
73
  }));
70
74
  } else {
71
75
  if (platform === 'mobile') {
@@ -16,7 +16,9 @@ export var EmbedCardResolvedView = /*#__PURE__*/React.forwardRef(function (_ref,
16
16
  isTrusted = _ref.isTrusted,
17
17
  _ref$testId = _ref.testId,
18
18
  testId = _ref$testId === void 0 ? 'embed-card-resolved-view' : _ref$testId,
19
- inheritDimensions = _ref.inheritDimensions;
19
+ inheritDimensions = _ref.inheritDimensions,
20
+ onIframeDwell = _ref.onIframeDwell,
21
+ onIframeFocus = _ref.onIframeFocus;
20
22
  var iconFromContext = context === null || context === void 0 ? void 0 : context.icon;
21
23
  var src = typeof iconFromContext === 'string' ? iconFromContext : undefined;
22
24
  var text = title || (context === null || context === void 0 ? void 0 : context.text);
@@ -50,6 +52,8 @@ export var EmbedCardResolvedView = /*#__PURE__*/React.forwardRef(function (_ref,
50
52
  url: preview === null || preview === void 0 ? void 0 : preview.src,
51
53
  isTrusted: isTrusted,
52
54
  testId: testId,
53
- ref: embedIframeRef
55
+ ref: embedIframeRef,
56
+ onIframeDwell: onIframeDwell,
57
+ onIframeFocus: onIframeFocus
54
58
  }));
55
59
  });
@@ -1,6 +1,10 @@
1
+ import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
2
+ import _regeneratorRuntime from "@babel/runtime/regenerator";
3
+
1
4
  /** @jsx jsx */
2
5
  import { jsx } from '@emotion/react';
3
6
  import ShortcutIcon from '@atlaskit/icon/glyph/shortcut';
7
+ import CopyIcon from '@atlaskit/icon/glyph/copy';
4
8
  import { extractType } from '@atlaskit/linking-common/extractors';
5
9
  import React, { useCallback, useEffect, useMemo } from 'react';
6
10
  import { ActionName, CardDisplay, SmartLinkPosition, SmartLinkSize } from '../../../constants';
@@ -38,6 +42,44 @@ export var getOpenAction = function getOpenAction(url, analytics, onActionClick)
38
42
  testId: 'hover-card-open-button'
39
43
  };
40
44
  };
45
+ export var getCopyAction = function getCopyAction(url) {
46
+ return {
47
+ name: ActionName.CustomAction,
48
+ icon: jsx(CopyIcon, {
49
+ label: "copy url",
50
+ size: "medium"
51
+ }),
52
+ iconPosition: 'before',
53
+ onClick: function () {
54
+ var _onClick = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
55
+ return _regeneratorRuntime.wrap(function _callee$(_context) {
56
+ while (1) {
57
+ switch (_context.prev = _context.next) {
58
+ case 0:
59
+ _context.next = 2;
60
+ return navigator.clipboard.writeText(url);
61
+
62
+ case 2:
63
+ return _context.abrupt("return", _context.sent);
64
+
65
+ case 3:
66
+ case "end":
67
+ return _context.stop();
68
+ }
69
+ }
70
+ }, _callee);
71
+ }));
72
+
73
+ function onClick() {
74
+ return _onClick.apply(this, arguments);
75
+ }
76
+
77
+ return onClick;
78
+ }(),
79
+ tooltipMessage: jsx(FormattedMessage, messages.copy_url_to_clipboard),
80
+ testId: 'hover-card-copy-button'
81
+ };
82
+ };
41
83
 
42
84
  var HoverCardContent = function HoverCardContent(_ref) {
43
85
  var _cardState$details;
@@ -72,7 +114,7 @@ var HoverCardContent = function HoverCardContent(_ref) {
72
114
  });
73
115
  }, [cardState.status, analytics.ui, id]);
74
116
  var titleActions = useMemo(function () {
75
- return [getOpenAction(url, analytics, onActionClick)];
117
+ return [getCopyAction(url), getOpenAction(url, analytics, onActionClick)];
76
118
  }, [url, analytics, onActionClick]);
77
119
  var data = (_cardState$details = cardState.details) === null || _cardState$details === void 0 ? void 0 : _cardState$details.data;
78
120
  var types = data ? extractType(data) : undefined;
@@ -1,6 +1,6 @@
1
1
  import { MessageDescriptor } from 'react-intl-next';
2
2
  export declare type RequestAccessMessageKey = 'click_to_join' | 'click_to_join_description' | 'forbidden_description' | 'request_access' | 'request_access_description' | 'request_access_pending' | 'request_access_pending_description' | 'request_denied_description';
3
- export declare type MessageKey = 'cannot_find_link' | 'connect_link_account_card' | 'connect_link_account_card_name' | 'connect_link_account_card_description' | 'connect_unauthorised_account_action' | 'connect_unauthorised_account_description' | 'connect_unauthorised_account_description_no_provider' | 'could_not_load_link' | 'download' | 'invalid_permissions' | 'invalid_permissions_description' | 'join_to_view' | 'connect_link_account' | 'created_by' | 'created_on_relative' | 'created_on_absolute' | 'delete' | 'edit' | 'learn_more_about_smart_links' | 'loading' | 'modified_by' | 'modified_on_relative' | 'modified_on_absolute' | 'more_actions' | 'not_found_title' | 'not_found_description' | 'open_link_in_a_new_tab' | 'preview' | 'preview_close' | 'preview_max_size' | 'preview_min_size' | 'priority_blocker' | 'priority_critical' | 'priority_high' | 'priority_highest' | 'priority_low' | 'priority_lowest' | 'priority_major' | 'priority_medium' | 'priority_minor' | 'priority_trivial' | 'priority_undefined' | 'forbidden_access' | 'pending_request' | 'restricted_link' | 'request_access_to_view' | 'request_denied' | 'try_again' | 'try_another_account' | 'view' | 'viewIn' | 'viewOriginal' | 'actions' | 'add_account' | 'cancel' | 'close' | 'connect_to' | 'connect_account_description' | 'retry' | 'save' | 'unlink_account' | RequestAccessMessageKey;
3
+ export declare type MessageKey = 'cannot_find_link' | 'connect_link_account_card' | 'connect_link_account_card_name' | 'connect_link_account_card_description' | 'connect_unauthorised_account_action' | 'connect_unauthorised_account_description' | 'connect_unauthorised_account_description_no_provider' | 'copy_url_to_clipboard' | 'could_not_load_link' | 'download' | 'invalid_permissions' | 'invalid_permissions_description' | 'join_to_view' | 'connect_link_account' | 'created_by' | 'created_on_relative' | 'created_on_absolute' | 'delete' | 'edit' | 'learn_more_about_smart_links' | 'loading' | 'modified_by' | 'modified_on_relative' | 'modified_on_absolute' | 'more_actions' | 'not_found_title' | 'not_found_description' | 'open_link_in_a_new_tab' | 'preview' | 'preview_close' | 'preview_max_size' | 'preview_min_size' | 'priority_blocker' | 'priority_critical' | 'priority_high' | 'priority_highest' | 'priority_low' | 'priority_lowest' | 'priority_major' | 'priority_medium' | 'priority_minor' | 'priority_trivial' | 'priority_undefined' | 'forbidden_access' | 'pending_request' | 'restricted_link' | 'request_access_to_view' | 'request_denied' | 'try_again' | 'try_another_account' | 'view' | 'viewIn' | 'viewOriginal' | 'actions' | 'add_account' | 'cancel' | 'close' | 'connect_to' | 'connect_account_description' | 'retry' | 'save' | 'unlink_account' | RequestAccessMessageKey;
4
4
  declare type Messages = {
5
5
  [K in MessageKey]: MessageDescriptor;
6
6
  };