@atlaskit/smart-card 15.5.0 → 16.2.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 (117) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/dist/cjs/client/index.js +30 -30
  3. package/dist/cjs/client/utils/environments.js +1 -1
  4. package/dist/cjs/extractors/block/index.js +4 -4
  5. package/dist/cjs/extractors/common/actions/extractPreviewAction.js +4 -3
  6. package/dist/cjs/in-product.js +43 -0
  7. package/dist/cjs/index.js +66 -4
  8. package/dist/cjs/providers/editor/index.js +113 -78
  9. package/dist/cjs/providers/editor/transformer.js +1 -1
  10. package/dist/cjs/state/actions/constants.js +1 -5
  11. package/dist/cjs/state/actions/helpers.js +1 -3
  12. package/dist/cjs/state/actions/index.js +16 -13
  13. package/dist/cjs/state/analytics/index.js +32 -84
  14. package/dist/cjs/state/analytics/ufoExperiences.js +60 -0
  15. package/dist/cjs/state/analytics/useSmartLinkAnalytics.js +106 -0
  16. package/dist/cjs/state/context/provider.js +3 -16
  17. package/dist/cjs/state/helpers.js +1 -2
  18. package/dist/cjs/state/hooks/useSmartLink.js +6 -6
  19. package/dist/cjs/state/reducers/index.js +3 -8
  20. package/dist/cjs/utils/analytics/analytics.js +31 -1
  21. package/dist/cjs/version.json +1 -1
  22. package/dist/cjs/view/BlockCard/index.js +3 -2
  23. package/dist/cjs/view/CardWithUrl/component-lazy/LazyFallback.js +6 -2
  24. package/dist/cjs/view/CardWithUrl/component-lazy/index.js +1 -1
  25. package/dist/cjs/view/CardWithUrl/component.js +10 -4
  26. package/dist/cjs/view/CardWithUrl/loader.js +110 -133
  27. package/dist/cjs/view/EmbedCard/{IframelyResizeMessageListener.js → EmbedResizeMessageListener.js} +11 -19
  28. package/dist/cjs/view/SmartLinkEvents/useSmartLinkEvents.js +21 -0
  29. package/dist/es2019/client/index.js +47 -43
  30. package/dist/es2019/client/utils/environments.js +1 -1
  31. package/dist/es2019/extractors/block/index.js +4 -4
  32. package/dist/es2019/extractors/common/actions/extractPreviewAction.js +4 -4
  33. package/dist/es2019/in-product.js +1 -0
  34. package/dist/es2019/index.js +7 -2
  35. package/dist/es2019/providers/editor/index.js +18 -6
  36. package/dist/es2019/providers/editor/transformer.js +1 -1
  37. package/dist/es2019/state/actions/constants.js +0 -2
  38. package/dist/es2019/state/actions/helpers.js +1 -3
  39. package/dist/es2019/state/actions/index.js +12 -11
  40. package/dist/es2019/state/analytics/index.js +2 -40
  41. package/dist/es2019/state/analytics/ufoExperiences.js +40 -0
  42. package/dist/es2019/state/analytics/useSmartLinkAnalytics.js +62 -0
  43. package/dist/es2019/state/context/provider.js +2 -8
  44. package/dist/es2019/state/helpers.js +1 -2
  45. package/dist/es2019/state/hooks/useSmartLink.js +4 -5
  46. package/dist/es2019/state/reducers/index.js +3 -8
  47. package/dist/es2019/utils/analytics/analytics.js +16 -0
  48. package/dist/es2019/version.json +1 -1
  49. package/dist/es2019/view/BlockCard/index.js +3 -2
  50. package/dist/es2019/view/CardWithUrl/component-lazy/LazyFallback.js +3 -1
  51. package/dist/es2019/view/CardWithUrl/component-lazy/index.js +1 -1
  52. package/dist/es2019/view/CardWithUrl/component.js +10 -4
  53. package/dist/es2019/view/CardWithUrl/loader.js +87 -89
  54. package/dist/es2019/view/EmbedCard/{IframelyResizeMessageListener.js → EmbedResizeMessageListener.js} +3 -10
  55. package/dist/es2019/view/SmartLinkEvents/useSmartLinkEvents.js +10 -0
  56. package/dist/esm/client/index.js +30 -31
  57. package/dist/esm/client/utils/environments.js +1 -1
  58. package/dist/esm/extractors/block/index.js +4 -4
  59. package/dist/esm/extractors/common/actions/extractPreviewAction.js +4 -3
  60. package/dist/esm/in-product.js +1 -0
  61. package/dist/esm/index.js +7 -2
  62. package/dist/esm/providers/editor/index.js +110 -78
  63. package/dist/esm/providers/editor/transformer.js +1 -1
  64. package/dist/esm/state/actions/constants.js +0 -2
  65. package/dist/esm/state/actions/helpers.js +1 -3
  66. package/dist/esm/state/actions/index.js +15 -13
  67. package/dist/esm/state/analytics/index.js +2 -80
  68. package/dist/esm/state/analytics/ufoExperiences.js +40 -0
  69. package/dist/esm/state/analytics/useSmartLinkAnalytics.js +94 -0
  70. package/dist/esm/state/context/provider.js +3 -15
  71. package/dist/esm/state/helpers.js +1 -2
  72. package/dist/esm/state/hooks/useSmartLink.js +6 -6
  73. package/dist/esm/state/reducers/index.js +3 -8
  74. package/dist/esm/utils/analytics/analytics.js +26 -0
  75. package/dist/esm/version.json +1 -1
  76. package/dist/esm/view/BlockCard/index.js +3 -2
  77. package/dist/esm/view/CardWithUrl/component-lazy/LazyFallback.js +3 -1
  78. package/dist/esm/view/CardWithUrl/component-lazy/index.js +1 -1
  79. package/dist/esm/view/CardWithUrl/component.js +10 -4
  80. package/dist/esm/view/CardWithUrl/loader.js +103 -127
  81. package/dist/esm/view/EmbedCard/{IframelyResizeMessageListener.js → EmbedResizeMessageListener.js} +9 -16
  82. package/dist/esm/view/SmartLinkEvents/useSmartLinkEvents.js +12 -0
  83. package/dist/types/__tests_external__/index.d.ts +6 -0
  84. package/dist/types/__tests_external__/page-objects/BaseCard.d.ts +18 -0
  85. package/dist/types/__tests_external__/page-objects/BlockCard.d.ts +6 -0
  86. package/dist/types/__tests_external__/page-objects/EmbedCard.d.ts +6 -0
  87. package/dist/types/__tests_external__/page-objects/InlineCard.d.ts +6 -0
  88. package/dist/types/__tests_external__/test-cases/card.d.ts +3 -0
  89. package/dist/types/__tests_external__/test-cases/embed.d.ts +3 -0
  90. package/dist/types/__tests_external__/test-cases/inline.d.ts +3 -0
  91. package/dist/types/__tests_external__/test-cases/types.d.ts +5 -0
  92. package/dist/types/client/index.d.ts +1 -0
  93. package/dist/types/extractors/block/index.d.ts +2 -2
  94. package/dist/types/extractors/common/__mocks__/jsonld.d.ts +61 -0
  95. package/dist/types/extractors/common/actions/extractPreviewAction.d.ts +1 -1
  96. package/dist/types/in-product.d.ts +1 -0
  97. package/dist/types/index.d.ts +5 -1
  98. package/dist/types/providers/editor/index.d.ts +3 -0
  99. package/dist/types/state/actions/constants.d.ts +0 -2
  100. package/dist/types/state/actions/types.d.ts +0 -2
  101. package/dist/types/state/analytics/index.d.ts +3 -32
  102. package/dist/types/state/analytics/ufoExperiences.d.ts +7 -0
  103. package/dist/types/state/analytics/useSmartLinkAnalytics.d.ts +30 -0
  104. package/dist/types/state/context/provider.d.ts +1 -1
  105. package/dist/types/state/context/types.d.ts +4 -2
  106. package/dist/types/state/hooks/useSmartLink.d.ts +4 -5
  107. package/dist/types/state/store/types.d.ts +2 -1
  108. package/dist/types/utils/analytics/analytics.d.ts +3 -0
  109. package/dist/types/utils/types.d.ts +1 -1
  110. package/dist/types/view/BlockCard/types.d.ts +2 -1
  111. package/dist/types/view/CardWithUrl/component-lazy/LazyFallback.d.ts +1 -0
  112. package/dist/types/view/CardWithUrl/component-lazy/index.d.ts +1 -1
  113. package/dist/types/view/CardWithUrl/loader.d.ts +1 -15
  114. package/dist/types/view/EmbedCard/{IframelyResizeMessageListener.d.ts → EmbedResizeMessageListener.d.ts} +1 -1
  115. package/dist/types/view/SmartLinkEvents/useSmartLinkEvents.d.ts +2 -0
  116. package/in-product/package.json +7 -0
  117. package/package.json +25 -20
@@ -7,6 +7,7 @@ import { useSmartLinkContext } from '../context';
7
7
  import * as measure from '../../utils/performance';
8
8
  import { APIError } from '../../client/errors';
9
9
  import { getUnauthorizedJsonLd } from '../../utils/jsonld';
10
+ import { addMetadataToExperience } from '../analytics';
10
11
  export const useSmartCardActions = (id, url, analytics) => {
11
12
  const {
12
13
  store,
@@ -19,16 +20,13 @@ export const useSmartCardActions = (id, url, analytics) => {
19
20
  } = store;
20
21
  const {
21
22
  details,
22
- lastUpdatedAt,
23
23
  status
24
24
  } = getState()[url] || {
25
25
  status: 'pending',
26
- lastUpdatedAt: Date.now(),
27
26
  details: undefined
28
27
  };
29
28
  const hasAuthFlowSupported = config.authFlow !== 'disabled';
30
29
  const hasData = !!(details && details.data);
31
- const hasExpired = Date.now() - lastUpdatedAt >= config.maxAge;
32
30
  const handleResolvedLinkError = useCallback((url, error) => {
33
31
  if (error.kind === 'fatal') {
34
32
  // If there's no previous data in the store for this URL, then bail
@@ -85,18 +83,21 @@ export const useSmartCardActions = (id, url, analytics) => {
85
83
 
86
84
 
87
85
  dispatch(cardAction(ACTION_RESOLVED, {
88
- url: resourceUrl,
89
- hasExpired: hasExpired && !hasData
86
+ url: resourceUrl
90
87
  }, response));
91
- }, [dispatch, handleResolvedLinkError, hasData, hasExpired, hasAuthFlowSupported]);
88
+ }, [dispatch, handleResolvedLinkError, hasAuthFlowSupported]);
92
89
  const resolve = useCallback(async (resourceUrl = url, isReloading = false) => {
93
90
  // Request JSON-LD data for the card from ORS, iff it has extended
94
91
  // its cache lifespan OR there is no data for it currently. Once the data
95
92
  // has come back asynchronously, dispatch the resolved action for the card.
96
- if (isReloading || hasExpired || !hasData) {
93
+ if (isReloading || !hasData) {
97
94
  return connections.client.fetchData(resourceUrl).then(response => handleResolvedLinkResponse(resourceUrl, response)).catch(error => handleResolvedLinkError(resourceUrl, error));
95
+ } else {
96
+ addMetadataToExperience('smart-link-rendered', id, {
97
+ cached: true
98
+ });
98
99
  }
99
- }, [url, hasData, hasExpired, connections.client, handleResolvedLinkResponse, handleResolvedLinkError]);
100
+ }, [url, hasData, connections.client, handleResolvedLinkResponse, handleResolvedLinkError, id]);
100
101
  const register = useCallback(() => {
101
102
  if (!details) {
102
103
  dispatch(cardAction(ACTION_PENDING, {
@@ -132,10 +133,10 @@ export const useSmartCardActions = (id, url, analytics) => {
132
133
  analytics.screen.authPopupEvent(definitionId, extensionKey);
133
134
  auth(services[0].url).then(() => {
134
135
  analytics.track.appAccountConnected(definitionId, extensionKey);
135
- analytics.operational.connectSucceededEvent(definitionId, extensionKey);
136
+ analytics.operational.connectSucceededEvent(id, definitionId, extensionKey);
136
137
  reload();
137
138
  }, err => {
138
- analytics.operational.connectFailedEvent(definitionId, extensionKey, err.type);
139
+ analytics.operational.connectFailedEvent(id, definitionId, extensionKey, err.type);
139
140
 
140
141
  if (err.type === 'auth_window_closed') {
141
142
  analytics.ui.closedAuthEvent(appearance, definitionId, extensionKey);
@@ -144,7 +145,7 @@ export const useSmartCardActions = (id, url, analytics) => {
144
145
  reload();
145
146
  });
146
147
  }
147
- }, [analytics.ui, analytics.screen, analytics.track, analytics.operational, reload, details, status]);
148
+ }, [analytics.ui, analytics.screen, analytics.track, analytics.operational, id, reload, details, status]);
148
149
  const invoke = useCallback(async (opts, appearance) => {
149
150
  const {
150
151
  key,
@@ -1,40 +1,2 @@
1
- import { uiAuthEvent, uiAuthAlternateAccountEvent, uiCardClickedEvent, uiActionClickedEvent, uiClosedAuthEvent, uiRenderSuccessEvent, uiRenderFailedEvent, resolvedEvent, unresolvedEvent, invokeSucceededEvent, invokeFailedEvent, connectSucceededEvent, connectFailedEvent, trackAppAccountConnected, screenAuthPopupEvent, instrumentEvent } from '../../utils/analytics';
2
- import { useMemo } from 'react';
3
- export const useSmartLinkAnalytics = dispatchAnalytics => {
4
- const ui = useMemo(() => ({
5
- authEvent: (display, definitionId, extensionKey) => dispatchAnalytics(uiAuthEvent(display, definitionId, extensionKey)),
6
- authAlternateAccountEvent: (display, definitionId, extensionKey) => dispatchAnalytics(uiAuthAlternateAccountEvent(display, definitionId, extensionKey)),
7
- cardClickedEvent: (display, definitionId, extensionKey) => dispatchAnalytics(uiCardClickedEvent(display, definitionId, extensionKey)),
8
- actionClickedEvent: (providerKey, actionType, display) => dispatchAnalytics(uiActionClickedEvent(providerKey, actionType, display)),
9
- closedAuthEvent: (display, definitionId, extensionKey) => dispatchAnalytics(uiClosedAuthEvent(display, definitionId, extensionKey)),
10
- renderSuccessEvent: (display, definitionId, extensionKey) => dispatchAnalytics(uiRenderSuccessEvent(display, definitionId, extensionKey)),
11
- renderFailedEvent: (display, error, errorInfo) => dispatchAnalytics(uiRenderFailedEvent(display, error, errorInfo))
12
- }), [dispatchAnalytics]);
13
- const operational = useMemo(() => ({
14
- resolvedEvent: (id, definitionId, extensionKey, resourceType) => dispatchAnalytics(resolvedEvent(id, definitionId, extensionKey, resourceType)),
15
- unresolvedEvent: (id, status, definitionId, extensionKey, resourceType) => dispatchAnalytics(unresolvedEvent(id, status, definitionId, extensionKey, resourceType)),
16
- invokeSucceededEvent: (id, providerKey, actionType, display) => dispatchAnalytics(invokeSucceededEvent(id, providerKey, actionType, display)),
17
- invokeFailedEvent: (id, providerKey, actionType, display, reason) => dispatchAnalytics(invokeFailedEvent(id, providerKey, actionType, display, reason)),
18
- connectSucceededEvent: (definitionId, extensionKey) => dispatchAnalytics(connectSucceededEvent(definitionId, extensionKey)),
19
- connectFailedEvent: (definitionId, extensionKey, reason) => dispatchAnalytics(connectFailedEvent(definitionId, extensionKey, reason)),
20
- instrument: (id, status, definitionId, extensionKey, resourceType, error) => {
21
- const event = instrumentEvent(id, status, definitionId, extensionKey, resourceType, error);
22
-
23
- if (event) {
24
- dispatchAnalytics(event);
25
- }
26
- }
27
- }), [dispatchAnalytics]);
28
- const track = useMemo(() => ({
29
- appAccountConnected: (definitionId, extensionKey) => dispatchAnalytics(trackAppAccountConnected(definitionId, extensionKey))
30
- }), [dispatchAnalytics]);
31
- const screen = useMemo(() => ({
32
- authPopupEvent: (definitionId, extensionKey) => dispatchAnalytics(screenAuthPopupEvent(definitionId, extensionKey))
33
- }), [dispatchAnalytics]);
34
- return useMemo(() => ({
35
- ui,
36
- operational,
37
- track,
38
- screen
39
- }), [ui, operational, track, screen]);
40
- };
1
+ export { startUfoExperience, succeedUfoExperience, failUfoExperience, addMetadataToExperience } from './ufoExperiences';
2
+ export { useSmartLinkAnalytics } from './useSmartLinkAnalytics';
@@ -0,0 +1,40 @@
1
+ import { ConcurrentExperience, ExperiencePerformanceTypes, ExperienceTypes } from '@atlaskit/ufo';
2
+ const ufoExperiences = {
3
+ 'smart-link-rendered': new ConcurrentExperience('smart-link-rendered', {
4
+ type: ExperienceTypes.Load,
5
+ performanceType: ExperiencePerformanceTypes.PageSegmentLoad,
6
+ platform: {
7
+ component: 'smart-links'
8
+ }
9
+ }),
10
+ 'smart-link-authenticated': new ConcurrentExperience('smart-link-authenticated', {
11
+ type: ExperienceTypes.Load,
12
+ performanceType: ExperiencePerformanceTypes.PageSegmentLoad,
13
+ platform: {
14
+ component: 'smart-links'
15
+ }
16
+ })
17
+ };
18
+ export const startUfoExperience = (experienceName, id, properties) => {
19
+ const experience = ufoExperiences[experienceName].getInstance(id);
20
+ experience.start();
21
+
22
+ if (properties) {
23
+ experience.addMetadata(properties);
24
+ }
25
+ };
26
+ export const succeedUfoExperience = (experienceName, id, properties) => {
27
+ const experience = ufoExperiences[experienceName].getInstance(id);
28
+ experience.success({
29
+ metadata: properties
30
+ });
31
+ };
32
+ export const failUfoExperience = (experienceName, id, properties) => {
33
+ const experience = ufoExperiences[experienceName].getInstance(id);
34
+ experience.failure({
35
+ metadata: properties
36
+ });
37
+ };
38
+ export const addMetadataToExperience = (experienceName, id, properties) => {
39
+ ufoExperiences[experienceName].getInstance(id).addMetadata(properties);
40
+ };
@@ -0,0 +1,62 @@
1
+ import { uiAuthEvent, uiAuthAlternateAccountEvent, uiCardClickedEvent, uiActionClickedEvent, uiClosedAuthEvent, uiRenderSuccessEvent, resolvedEvent, unresolvedEvent, invokeSucceededEvent, invokeFailedEvent, connectSucceededEvent, connectFailedEvent, trackAppAccountConnected, screenAuthPopupEvent, instrumentEvent } from '../../utils/analytics';
2
+ import { useMemo } from 'react';
3
+ import { startUfoExperience, succeedUfoExperience } from './ufoExperiences';
4
+ export const useSmartLinkAnalytics = dispatchAnalytics => {
5
+ const ui = useMemo(() => ({
6
+ authEvent: (display, definitionId, extensionKey) => dispatchAnalytics(uiAuthEvent(display, definitionId, extensionKey)),
7
+ authAlternateAccountEvent: (display, definitionId, extensionKey) => dispatchAnalytics(uiAuthAlternateAccountEvent(display, definitionId, extensionKey)),
8
+ cardClickedEvent: (display, definitionId, extensionKey) => dispatchAnalytics(uiCardClickedEvent(display, definitionId, extensionKey)),
9
+ actionClickedEvent: (providerKey, actionType, display) => dispatchAnalytics(uiActionClickedEvent(providerKey, actionType, display)),
10
+ closedAuthEvent: (display, definitionId, extensionKey) => dispatchAnalytics(uiClosedAuthEvent(display, definitionId, extensionKey)),
11
+ renderSuccessEvent: (display, id, definitionId, extensionKey) => {
12
+ succeedUfoExperience('smart-link-rendered', id, {
13
+ extensionKey,
14
+ display
15
+ }); // UFO will disregard this if authentication experience has not yet been started
16
+
17
+ succeedUfoExperience('smart-link-authenticated', id, {
18
+ display
19
+ });
20
+ dispatchAnalytics(uiRenderSuccessEvent(display, definitionId, extensionKey));
21
+ }
22
+ }), [dispatchAnalytics]);
23
+ const operational = useMemo(() => ({
24
+ resolvedEvent: (id, definitionId, extensionKey, resourceType) => dispatchAnalytics(resolvedEvent(id, definitionId, extensionKey, resourceType)),
25
+ unresolvedEvent: (id, status, definitionId, extensionKey, resourceType) => dispatchAnalytics(unresolvedEvent(id, status, definitionId, extensionKey, resourceType)),
26
+ invokeSucceededEvent: (id, providerKey, actionType, display) => dispatchAnalytics(invokeSucceededEvent(id, providerKey, actionType, display)),
27
+ invokeFailedEvent: (id, providerKey, actionType, display, reason) => dispatchAnalytics(invokeFailedEvent(id, providerKey, actionType, display, reason)),
28
+ connectSucceededEvent: (id, definitionId, extensionKey) => {
29
+ startUfoExperience('smart-link-authenticated', id, {
30
+ extensionKey,
31
+ status: 'success'
32
+ });
33
+ dispatchAnalytics(connectSucceededEvent(definitionId, extensionKey));
34
+ },
35
+ connectFailedEvent: (id, definitionId, extensionKey, status) => {
36
+ startUfoExperience('smart-link-authenticated', id, {
37
+ extensionKey,
38
+ status
39
+ });
40
+ dispatchAnalytics(connectFailedEvent(definitionId, extensionKey, status));
41
+ },
42
+ instrument: (id, status, definitionId, extensionKey, resourceType, error) => {
43
+ const event = instrumentEvent(id, status, definitionId, extensionKey, resourceType, error);
44
+
45
+ if (event) {
46
+ dispatchAnalytics(event);
47
+ }
48
+ }
49
+ }), [dispatchAnalytics]);
50
+ const track = useMemo(() => ({
51
+ appAccountConnected: (definitionId, extensionKey) => dispatchAnalytics(trackAppAccountConnected(definitionId, extensionKey))
52
+ }), [dispatchAnalytics]);
53
+ const screen = useMemo(() => ({
54
+ authPopupEvent: (definitionId, extensionKey) => dispatchAnalytics(screenAuthPopupEvent(definitionId, extensionKey))
55
+ }), [dispatchAnalytics]);
56
+ return useMemo(() => ({
57
+ ui,
58
+ operational,
59
+ track,
60
+ screen
61
+ }), [ui, operational, track, screen]);
62
+ };
@@ -3,14 +3,12 @@ import { useContext, useMemo } from 'react';
3
3
  import { createStore } from 'redux';
4
4
  import { cardReducer } from '../reducers';
5
5
  import { SmartCardContext } from '.';
6
- import { MAX_RELOAD_DELAY, MAX_LOADING_DELAY } from '../actions/constants';
7
6
  import CardClient from '../../client';
8
7
  import { extractPreview } from '../../extractors/common/preview';
9
8
  import { getUrl } from '../helpers';
10
9
  export function SmartCardProvider({
11
10
  storeOptions,
12
11
  client: customClient,
13
- cacheOptions: customCacheOptions,
14
12
  authFlow: customAuthFlow,
15
13
  children,
16
14
  renderers
@@ -25,10 +23,6 @@ export function SmartCardProvider({
25
23
  const client = customClient || new CardClient();
26
24
  const store = createStore(cardReducer, initialState);
27
25
  const authFlow = customAuthFlow || 'oauth2';
28
- const cacheOptions = customCacheOptions || {
29
- maxAge: MAX_RELOAD_DELAY,
30
- maxLoadingDelay: MAX_LOADING_DELAY
31
- };
32
26
 
33
27
  const getPreview = (url, platform) => {
34
28
  const cardState = getUrl(store, url);
@@ -42,14 +36,14 @@ export function SmartCardProvider({
42
36
  connections: {
43
37
  client
44
38
  },
45
- config: { ...cacheOptions,
39
+ config: {
46
40
  authFlow
47
41
  },
48
42
  extractors: {
49
43
  getPreview
50
44
  }
51
45
  };
52
- }, [customClient, customCacheOptions, customAuthFlow, storeOptions, renderers]);
46
+ }, [customClient, customAuthFlow, storeOptions, renderers]);
53
47
  return /*#__PURE__*/React.createElement(SmartCardContext.Provider, {
54
48
  value: parentContext || providerValue
55
49
  }, children);
@@ -10,8 +10,7 @@ export const getByDefinitionId = (definitionId, store) => {
10
10
  };
11
11
  export const getUrl = (store, url) => {
12
12
  return store.getState()[url] || {
13
- status: 'pending',
14
- lastUpdatedAt: Date.now()
13
+ status: 'pending'
15
14
  };
16
15
  };
17
16
  export const getClickUrl = (url, jsonLd) => {
@@ -11,12 +11,10 @@ export function useSmartLink(id, url, dispatchAnalytics) {
11
11
  const config = useSmartLinkConfig();
12
12
  const renderers = useSmartLinkRenderers(); // NB: used to propagate errors from hooks to error boundaries.
13
13
 
14
- const [, setState] = useState(); // Register the current card.
14
+ const [error, setError] = useState(null); // Register the current card.
15
15
 
16
16
  const register = () => {
17
- actions.register().catch(err => setState(() => {
18
- throw err;
19
- }));
17
+ actions.register().catch(err => setError(err));
20
18
  }; // AFP-2511 TODO: Fix automatic suppressions below
21
19
  // eslint-disable-next-line react-hooks/exhaustive-deps
22
20
 
@@ -28,6 +26,7 @@ export function useSmartLink(id, url, dispatchAnalytics) {
28
26
  actions,
29
27
  config,
30
28
  analytics,
31
- renderers
29
+ renderers,
30
+ error
32
31
  };
33
32
  }
@@ -5,8 +5,7 @@ const cardReducerMap = {
5
5
  type
6
6
  }) => {
7
7
  return {
8
- status: type,
9
- lastUpdatedAt: Date.now()
8
+ status: type
10
9
  };
11
10
  },
12
11
  [actions.ACTION_RESOLVING]: (state, {
@@ -33,7 +32,6 @@ const cardReducerMap = {
33
32
  nextState.status = type;
34
33
  }
35
34
 
36
- nextState.lastUpdatedAt = Date.now();
37
35
  return nextState;
38
36
  },
39
37
  [actions.ACTION_ERROR]: (state, {
@@ -60,12 +58,9 @@ export const cardReducer = (state, action) => {
60
58
  const cardState = state[action.url]; // Card may have reached the same state on account of multiple of the same
61
59
  // URL being present in an editor session. E.g. page with N links to one resource.
62
60
 
63
- const hasSameStatus = cardState && cardState.status === action.type; // Card needs to be refreshed. In this case, we ignore the same status pragma which
64
- // is described above - this forces a rerender for all links of the same status.
65
-
66
- const hasExpired = action.hasExpired;
61
+ const hasSameStatus = cardState && cardState.status === action.type;
67
62
 
68
- if (!hasExpired && hasSameStatus) {
63
+ if (hasSameStatus) {
69
64
  return state;
70
65
  } else {
71
66
  const nextState = cardReducerMap[action.type](cardState, action);
@@ -6,6 +6,22 @@ export const context = {
6
6
  packageName,
7
7
  packageVersion
8
8
  };
9
+ export class SmartLinkEvents {
10
+ insertSmartLink(url, type, createAnalyticsEvent) {
11
+ fireSmartLinkEvent({
12
+ action: 'inserted',
13
+ actionSubject: 'smartLink',
14
+ eventType: 'track',
15
+ attributes: {
16
+ type
17
+ },
18
+ nonPrivacySafeAttributes: {
19
+ domainName: url
20
+ }
21
+ }, createAnalyticsEvent);
22
+ }
23
+
24
+ }
9
25
  export const fireSmartLinkEvent = (payload, createAnalyticsEvent) => {
10
26
  if (createAnalyticsEvent) {
11
27
  createAnalyticsEvent(payload).fire(ANALYTICS_CHANNEL);
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/smart-card",
3
- "version": "15.5.0",
3
+ "version": "16.2.0",
4
4
  "sideEffects": false
5
5
  }
@@ -21,7 +21,8 @@ export const BlockCard = ({
21
21
  isSelected,
22
22
  onResolve,
23
23
  testId,
24
- showActions
24
+ showActions,
25
+ platform
25
26
  }) => {
26
27
  var _details$meta, _forbiddenViewProps$c;
27
28
 
@@ -42,7 +43,7 @@ export const BlockCard = ({
42
43
  });
43
44
 
44
45
  case 'resolved':
45
- const resolvedViewProps = extractBlockProps(data, meta, extractorOpts, renderers);
46
+ const resolvedViewProps = extractBlockProps(data, meta, extractorOpts, renderers, platform);
46
47
 
47
48
  if (onResolve) {
48
49
  onResolve({
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
2
  import { CardLinkView } from '@atlaskit/media-ui';
3
+ export const loadingPlaceholderClassName = 'smart-link-loading-placeholder';
3
4
  export const LoadingCardLink = ({
4
5
  isSelected,
5
6
  url
@@ -8,5 +9,6 @@ export const LoadingCardLink = ({
8
9
  testId: 'lazy-render-placeholder',
9
10
  "data-trello-do-not-use-override": "lazy-render-placeholder-trello",
10
11
  isSelected: isSelected,
11
- link: url
12
+ link: url,
13
+ className: loadingPlaceholderClassName
12
14
  });
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import { isIntersectionObserverSupported } from '@atlaskit/media-ui';
3
3
  import { LazyLazilyRenderCard } from './LazyLazilyRenderCard';
4
4
  import { LazyIntersectionObserverCard } from './LazyIntersectionObserverCard';
5
- export function LazyCardWithUrlContent(props) {
5
+ export default function LazyCardWithUrlContent(props) {
6
6
  if (isIntersectionObserverSupported()) {
7
7
  return /*#__PURE__*/React.createElement(LazyIntersectionObserverCard, props);
8
8
  } else {
@@ -28,7 +28,8 @@ export function CardWithUrlContent({
28
28
  actions,
29
29
  config,
30
30
  analytics,
31
- renderers
31
+ renderers,
32
+ error
32
33
  } = useSmartLink(id, url, dispatchAnalytics);
33
34
  const definitionId = getDefinitionId(state.details);
34
35
  const extensionKey = getExtensionKey(state.details);
@@ -67,9 +68,13 @@ export function CardWithUrlContent({
67
68
 
68
69
  useEffect(() => {
69
70
  if (isFinalState(state.status)) {
70
- analytics.ui.renderSuccessEvent(appearance, definitionId, extensionKey);
71
+ analytics.ui.renderSuccessEvent(appearance, id, definitionId, extensionKey);
71
72
  }
72
- }, [appearance, state.details, state.status, url, definitionId, extensionKey, analytics.ui]);
73
+ }, [appearance, state.details, state.status, url, definitionId, extensionKey, analytics.ui, id]); // We have to keep this last to prevent hook order from being violated
74
+
75
+ if (error) {
76
+ throw error;
77
+ }
73
78
 
74
79
  switch (appearance) {
75
80
  case 'inline':
@@ -99,7 +104,8 @@ export function CardWithUrlContent({
99
104
  isSelected: isSelected,
100
105
  onResolve: onResolve,
101
106
  testId: testId,
102
- showActions: showActions
107
+ showActions: showActions,
108
+ platform: platform
103
109
  });
104
110
 
105
111
  case 'embed':
@@ -1,60 +1,32 @@
1
- import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
- import React from 'react';
1
+ import React, { lazy, useEffect, useState, Suspense } from 'react';
3
2
  import uuid from 'uuid';
4
3
  import { CardLinkView } from '@atlaskit/media-ui';
5
4
  import { uiRenderFailedEvent, fireSmartLinkEvent } from '../../utils/analytics';
6
5
  import { clearMarks, clearMeasures } from '../../utils/performance';
7
- export class CardWithURLRenderer extends React.PureComponent {
8
- constructor(props) {
9
- super(props);
6
+ import { ErrorBoundary } from 'react-error-boundary';
7
+ import { failUfoExperience, startUfoExperience } from '../../state/analytics/ufoExperiences';
8
+ const LazyCardWithUrlContent = /*#__PURE__*/lazy(() => import(
9
+ /* webpackChunkName: "@atlaskit-internal_smartcard-urlcardcontent" */
10
+ './component-lazy/index'));
11
+ export function CardWithURLRenderer(props) {
12
+ const [id] = useState(() => uuid()); // Equivalent to ComponentWillMount
10
13
 
11
- _defineProperty(this, "dispatchAnalytics", analyticsPayload => {
12
- const {
13
- appearance,
14
- createAnalyticsEvent
15
- } = this.props;
16
-
17
- if (analyticsPayload && analyticsPayload.attributes) {
18
- // Update if we haven't already set the display - possible
19
- // in the case of `preview` which is rendered differently.
20
- if (!analyticsPayload.attributes.display) {
21
- analyticsPayload.attributes.display = appearance;
22
- }
23
- }
24
-
25
- fireSmartLinkEvent(analyticsPayload, createAnalyticsEvent);
26
- });
27
-
28
- this.state = {
29
- id: uuid()
14
+ useState(() => {
15
+ // We want to start timing of render event only once and right at the start
16
+ startUfoExperience('smart-link-rendered', id);
17
+ });
18
+ useEffect(() => {
19
+ // ComponentWillUnmount
20
+ return () => {
21
+ clearMarks(id);
22
+ clearMeasures(id);
30
23
  };
31
- }
24
+ }, [id]);
32
25
 
33
- static moduleImporter(target) {
34
- import(
35
- /* webpackChunkName: "@atlaskit-internal_smartcard-urlcardcontent" */
36
- './component-lazy/index').then(module => {
37
- CardWithURLRenderer.CardContent = module.LazyCardWithUrlContent;
38
- target.forceUpdate();
39
- });
40
- }
41
-
42
- componentDidMount() {
43
- if (CardWithURLRenderer.CardContent === null) {
44
- (this.props.importer || CardWithURLRenderer.moduleImporter)(this);
45
- }
46
- }
47
-
48
- componentWillUnmount() {
49
- const id = this.state.id;
50
- clearMarks(id);
51
- clearMeasures(id);
52
- }
53
-
54
- componentDidCatch(error, errorInfo) {
26
+ const logError = (error, info) => {
55
27
  const {
56
- appearance
57
- } = this.props; // NB: APIErrors are thrown in response to Object Resolver Service.
28
+ componentStack
29
+ } = info; // NB: APIErrors are thrown in response to Object Resolver Service.
58
30
  // In these cases, control is handed back to the Editor. We do not
59
31
  // fire an event for these, as they do not cover failed UI render events.
60
32
 
@@ -64,54 +36,80 @@ export class CardWithURLRenderer extends React.PureComponent {
64
36
  // to the reliability of the smart-card front-end components. We instrument
65
37
  // these in order to measure our front-end reliability.
66
38
  else {
67
- this.dispatchAnalytics(uiRenderFailedEvent(appearance, error, errorInfo));
68
- }
69
- } // Wrapper around analytics.
39
+ const errorInfo = {
40
+ componentStack
41
+ };
42
+ failUfoExperience('smart-link-rendered', id);
43
+ failUfoExperience('smart-link-authenticated', id);
44
+ dispatchAnalytics(uiRenderFailedEvent(appearance, error, errorInfo));
45
+ }
46
+ }; // Wrapper around analytics.
70
47
 
71
48
 
72
- render() {
49
+ const dispatchAnalytics = analyticsPayload => {
73
50
  const {
74
- url,
75
51
  appearance,
76
- isSelected,
77
- isFrameVisible,
78
- onClick,
79
- container,
80
- onResolve,
81
- testId,
82
- showActions,
83
- inheritDimensions,
84
- platform,
85
- embedIframeRef,
86
- inlinePreloaderStyle
87
- } = this.props;
52
+ createAnalyticsEvent
53
+ } = props;
88
54
 
89
- if (!url) {
90
- throw new Error('@atlaskit/smart-card: url property is missing.');
55
+ if (analyticsPayload && analyticsPayload.attributes) {
56
+ // Update if we haven't already set the display - possible
57
+ // in the case of `preview` which is rendered differently.
58
+ if (!analyticsPayload.attributes.display) {
59
+ analyticsPayload.attributes.display = appearance;
60
+ }
91
61
  }
92
62
 
93
- return CardWithURLRenderer.CardContent !== null ? /*#__PURE__*/React.createElement(CardWithURLRenderer.CardContent, {
94
- id: this.state.id,
95
- url: url,
96
- appearance: appearance,
97
- onClick: onClick,
98
- isSelected: isSelected,
99
- isFrameVisible: isFrameVisible,
100
- dispatchAnalytics: this.dispatchAnalytics,
101
- container: container,
102
- onResolve: onResolve,
103
- testId: testId,
104
- showActions: showActions,
105
- inheritDimensions: inheritDimensions,
106
- platform: platform,
107
- embedIframeRef: embedIframeRef,
108
- inlinePreloaderStyle: inlinePreloaderStyle
109
- }) : /*#__PURE__*/React.createElement(CardLinkView, {
110
- key: 'chunk-placeholder',
111
- link: url
112
- });
63
+ fireSmartLinkEvent(analyticsPayload, createAnalyticsEvent);
64
+ };
65
+
66
+ const {
67
+ url,
68
+ appearance,
69
+ isSelected,
70
+ isFrameVisible,
71
+ onClick,
72
+ container,
73
+ onResolve,
74
+ testId,
75
+ showActions,
76
+ inheritDimensions,
77
+ platform,
78
+ embedIframeRef,
79
+ inlinePreloaderStyle
80
+ } = props;
81
+
82
+ if (!url) {
83
+ throw new Error('@atlaskit/smart-card: url property is missing.');
113
84
  }
114
85
 
86
+ return /*#__PURE__*/React.createElement(ErrorBoundary, {
87
+ FallbackComponent: ErrorFallback,
88
+ onError: logError
89
+ }, /*#__PURE__*/React.createElement(Suspense, {
90
+ fallback: /*#__PURE__*/React.createElement(CardLinkView, {
91
+ key: 'chunk-placeholder',
92
+ link: url
93
+ })
94
+ }, /*#__PURE__*/React.createElement(LazyCardWithUrlContent, {
95
+ id: id,
96
+ url: url,
97
+ appearance: appearance,
98
+ onClick: onClick,
99
+ isSelected: isSelected,
100
+ isFrameVisible: isFrameVisible,
101
+ dispatchAnalytics: dispatchAnalytics,
102
+ container: container,
103
+ onResolve: onResolve,
104
+ testId: testId,
105
+ showActions: showActions,
106
+ inheritDimensions: inheritDimensions,
107
+ platform: platform,
108
+ embedIframeRef: embedIframeRef,
109
+ inlinePreloaderStyle: inlinePreloaderStyle
110
+ })));
115
111
  }
116
112
 
117
- _defineProperty(CardWithURLRenderer, "CardContent", null);
113
+ const ErrorFallback = () => {
114
+ return /*#__PURE__*/React.createElement("span", null);
115
+ };