@atlaskit/media-card 70.9.0 → 72.0.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 (181) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/dist/cjs/errors.js +109 -6
  3. package/dist/cjs/files/cardImageView/index.js +58 -89
  4. package/dist/cjs/files/index.js +0 -6
  5. package/dist/cjs/index.js +16 -6
  6. package/dist/cjs/root/card/cardAnalytics.js +33 -19
  7. package/dist/cjs/root/card/cardConstants.js +8 -0
  8. package/dist/cjs/root/card/cardSSRView.js +114 -0
  9. package/dist/cjs/root/card/cardState.js +50 -0
  10. package/dist/cjs/root/card/getCardPreview/cache.js +49 -0
  11. package/dist/cjs/root/card/getCardPreview/filePreviewStatus.js +50 -0
  12. package/dist/cjs/root/card/getCardPreview/helpers.js +215 -0
  13. package/dist/cjs/root/card/getCardPreview/index.js +225 -0
  14. package/dist/cjs/root/card/getCardStatus.js +16 -21
  15. package/dist/cjs/root/card/index.js +339 -398
  16. package/dist/cjs/root/cardView.js +108 -56
  17. package/dist/cjs/root/index.js +9 -1
  18. package/dist/cjs/root/inline/loader.js +22 -21
  19. package/dist/cjs/root/inline/{inlineMediaCard.js → mediaInlineCard.js} +66 -24
  20. package/dist/cjs/root/inlinePlayer.js +5 -15
  21. package/dist/cjs/root/ui/Breakpoint.js +13 -0
  22. package/dist/cjs/root/ui/common.js +5 -11
  23. package/dist/cjs/root/ui/iconMessage/index.js +17 -9
  24. package/dist/cjs/root/ui/imageRenderer/imageRenderer.js +32 -115
  25. package/dist/cjs/root/ui/loadingRateLimited/loadingRateLimited.js +2 -2
  26. package/dist/cjs/root/ui/progressBar/progressBar.js +2 -2
  27. package/dist/cjs/root/ui/progressBar/styled.js +3 -1
  28. package/dist/cjs/root/ui/styled.js +17 -80
  29. package/dist/cjs/root/ui/styledSSR.js +108 -0
  30. package/dist/cjs/root/ui/titleBox/failedTitleBox.js +7 -3
  31. package/dist/cjs/root/ui/titleBox/styled.js +3 -1
  32. package/dist/cjs/utils/analytics.js +26 -43
  33. package/dist/cjs/utils/dimensionComparer.js +1 -1
  34. package/dist/cjs/utils/document.js +12 -0
  35. package/dist/cjs/utils/getDataURIDimension.js +13 -2
  36. package/dist/cjs/utils/metadata.js +11 -3
  37. package/dist/cjs/utils/objectURLCache.js +6 -0
  38. package/dist/cjs/utils/resizeModeToMediaImageProps.js +13 -0
  39. package/dist/cjs/utils/shouldDisplayImageThumbnail.js +1 -1
  40. package/dist/cjs/utils/videoSnapshot.js +45 -0
  41. package/dist/cjs/version.json +1 -1
  42. package/dist/es2019/errors.js +52 -2
  43. package/dist/es2019/files/cardImageView/index.js +12 -46
  44. package/dist/es2019/files/index.js +1 -1
  45. package/dist/es2019/index.js +3 -3
  46. package/dist/es2019/root/card/cardAnalytics.js +23 -17
  47. package/dist/es2019/root/card/cardConstants.js +1 -0
  48. package/dist/es2019/root/card/cardSSRView.js +93 -0
  49. package/dist/es2019/root/card/cardState.js +26 -0
  50. package/dist/es2019/root/card/getCardPreview/cache.js +29 -0
  51. package/dist/es2019/root/card/getCardPreview/filePreviewStatus.js +35 -0
  52. package/dist/es2019/root/card/getCardPreview/helpers.js +83 -0
  53. package/dist/es2019/root/card/getCardPreview/index.js +129 -0
  54. package/dist/es2019/root/card/getCardStatus.js +11 -15
  55. package/dist/es2019/root/card/index.js +292 -276
  56. package/dist/es2019/root/cardView.js +107 -53
  57. package/dist/es2019/root/index.js +2 -1
  58. package/dist/es2019/root/inline/loader.js +16 -15
  59. package/dist/es2019/root/inline/mediaInlineCard.js +127 -0
  60. package/dist/es2019/root/inlinePlayer.js +5 -13
  61. package/dist/es2019/root/ui/Breakpoint.js +6 -0
  62. package/dist/es2019/root/ui/common.js +1 -7
  63. package/dist/es2019/root/ui/iconMessage/index.js +10 -6
  64. package/dist/es2019/root/ui/imageRenderer/imageRenderer.js +22 -74
  65. package/dist/es2019/root/ui/loadingRateLimited/loadingRateLimited.js +1 -1
  66. package/dist/es2019/root/ui/progressBar/progressBar.js +1 -1
  67. package/dist/es2019/root/ui/progressBar/styled.js +2 -1
  68. package/dist/es2019/root/ui/styled.js +4 -64
  69. package/dist/es2019/root/ui/styledSSR.js +93 -0
  70. package/dist/es2019/root/ui/titleBox/failedTitleBox.js +5 -3
  71. package/dist/es2019/root/ui/titleBox/styled.js +2 -1
  72. package/dist/es2019/utils/analytics.js +29 -40
  73. package/dist/es2019/utils/dimensionComparer.js +1 -1
  74. package/dist/es2019/utils/document.js +1 -0
  75. package/dist/es2019/utils/getDataURIDimension.js +8 -0
  76. package/dist/es2019/utils/metadata.js +12 -4
  77. package/dist/es2019/utils/objectURLCache.js +5 -0
  78. package/dist/es2019/utils/resizeModeToMediaImageProps.js +6 -0
  79. package/dist/es2019/utils/shouldDisplayImageThumbnail.js +1 -1
  80. package/dist/es2019/utils/videoSnapshot.js +7 -0
  81. package/dist/es2019/version.json +1 -1
  82. package/dist/esm/errors.js +85 -2
  83. package/dist/esm/files/cardImageView/index.js +55 -87
  84. package/dist/esm/files/index.js +1 -1
  85. package/dist/esm/index.js +3 -3
  86. package/dist/esm/root/card/cardAnalytics.js +23 -18
  87. package/dist/esm/root/card/cardConstants.js +1 -0
  88. package/dist/esm/root/card/cardSSRView.js +92 -0
  89. package/dist/esm/root/card/cardState.js +32 -0
  90. package/dist/esm/root/card/getCardPreview/cache.js +33 -0
  91. package/dist/esm/root/card/getCardPreview/filePreviewStatus.js +35 -0
  92. package/dist/esm/root/card/getCardPreview/helpers.js +194 -0
  93. package/dist/esm/root/card/getCardPreview/index.js +174 -0
  94. package/dist/esm/root/card/getCardStatus.js +13 -17
  95. package/dist/esm/root/card/index.js +349 -401
  96. package/dist/esm/root/cardView.js +105 -52
  97. package/dist/esm/root/index.js +2 -1
  98. package/dist/esm/root/inline/loader.js +23 -22
  99. package/dist/esm/root/inline/{inlineMediaCard.js → mediaInlineCard.js} +62 -22
  100. package/dist/esm/root/inlinePlayer.js +5 -13
  101. package/dist/esm/root/ui/Breakpoint.js +6 -0
  102. package/dist/esm/root/ui/common.js +1 -7
  103. package/dist/esm/root/ui/iconMessage/index.js +12 -7
  104. package/dist/esm/root/ui/imageRenderer/imageRenderer.js +24 -106
  105. package/dist/esm/root/ui/loadingRateLimited/loadingRateLimited.js +1 -1
  106. package/dist/esm/root/ui/progressBar/progressBar.js +1 -1
  107. package/dist/esm/root/ui/progressBar/styled.js +2 -1
  108. package/dist/esm/root/ui/styled.js +13 -61
  109. package/dist/esm/root/ui/styledSSR.js +76 -0
  110. package/dist/esm/root/ui/titleBox/failedTitleBox.js +6 -3
  111. package/dist/esm/root/ui/titleBox/styled.js +2 -1
  112. package/dist/esm/utils/analytics.js +22 -35
  113. package/dist/esm/utils/dimensionComparer.js +1 -1
  114. package/dist/esm/utils/document.js +3 -0
  115. package/dist/esm/utils/getDataURIDimension.js +8 -0
  116. package/dist/esm/utils/metadata.js +12 -4
  117. package/dist/esm/utils/objectURLCache.js +6 -0
  118. package/dist/esm/utils/resizeModeToMediaImageProps.js +6 -0
  119. package/dist/esm/utils/shouldDisplayImageThumbnail.js +1 -1
  120. package/dist/esm/utils/videoSnapshot.js +31 -0
  121. package/dist/esm/version.json +1 -1
  122. package/dist/types/__tests_external__/page-objects/MediaCard.d.ts +4 -3
  123. package/dist/types/errors.d.ts +24 -2
  124. package/dist/types/files/cardImageView/index.d.ts +5 -12
  125. package/dist/types/files/cardImageView/styled.d.ts +1 -1
  126. package/dist/types/files/index.d.ts +1 -1
  127. package/dist/types/index.d.ts +9 -12
  128. package/dist/types/root/card/cardAnalytics.d.ts +5 -7
  129. package/dist/types/root/card/cardConstants.d.ts +1 -0
  130. package/dist/types/root/card/cardSSRView.d.ts +13 -0
  131. package/dist/types/root/card/cardState.d.ts +5 -0
  132. package/dist/types/root/card/getCardPreview/cache.d.ts +18 -0
  133. package/dist/types/root/card/getCardPreview/filePreviewStatus.d.ts +5 -0
  134. package/dist/types/root/card/getCardPreview/helpers.d.ts +9 -0
  135. package/dist/types/root/card/getCardPreview/index.d.ts +44 -0
  136. package/dist/types/root/card/getCardStatus.d.ts +4 -14
  137. package/dist/types/root/card/index.d.ts +17 -21
  138. package/dist/types/root/cardView.d.ts +12 -8
  139. package/dist/types/root/index.d.ts +1 -0
  140. package/dist/types/root/inline/loader.d.ts +8 -8
  141. package/dist/types/root/inline/{inlineMediaCard.d.ts → mediaInlineCard.d.ts} +4 -4
  142. package/dist/types/root/inlinePlayer.d.ts +1 -1
  143. package/dist/types/root/ui/Breakpoint.d.ts +4 -0
  144. package/dist/types/root/ui/common.d.ts +1 -4
  145. package/dist/types/root/ui/iconMessage/index.d.ts +7 -2
  146. package/dist/types/root/ui/iconWrapper/styled.d.ts +1 -1
  147. package/dist/types/root/ui/imageRenderer/imageRenderer.d.ts +2 -16
  148. package/dist/types/root/ui/loadingRateLimited/loadingRateLimited.d.ts +1 -1
  149. package/dist/types/root/ui/loadingRateLimited/styled.d.ts +1 -1
  150. package/dist/types/root/ui/progressBar/progressBar.d.ts +1 -1
  151. package/dist/types/root/ui/progressBar/styled.d.ts +1 -1
  152. package/dist/types/root/ui/styled.d.ts +3 -10
  153. package/dist/types/root/ui/styledSSR.d.ts +16 -0
  154. package/dist/types/root/ui/titleBox/failedTitleBox.d.ts +3 -1
  155. package/dist/types/root/ui/titleBox/styled.d.ts +1 -1
  156. package/dist/types/root/ui/titleBox/titleBox.d.ts +1 -1
  157. package/dist/types/types.d.ts +15 -1
  158. package/dist/types/utils/analytics.d.ts +20 -21
  159. package/dist/types/utils/cardDimensions.d.ts +5 -1
  160. package/dist/types/utils/dimensionComparer.d.ts +1 -1
  161. package/dist/types/utils/document.d.ts +2 -0
  162. package/dist/types/utils/getDataURIDimension.d.ts +3 -1
  163. package/dist/types/utils/index.d.ts +1 -0
  164. package/dist/types/utils/lazyContent/styled.d.ts +1 -1
  165. package/dist/types/utils/lightCards/types.d.ts +1 -1
  166. package/dist/types/utils/metadata.d.ts +2 -2
  167. package/dist/types/utils/objectURLCache.d.ts +2 -1
  168. package/dist/types/utils/resizeModeToMediaImageProps.d.ts +5 -0
  169. package/dist/types/utils/shouldDisplayImageThumbnail.d.ts +1 -1
  170. package/dist/types/utils/videoSnapshot.d.ts +1 -0
  171. package/example-helpers/index.tsx +21 -0
  172. package/package.json +19 -17
  173. package/dist/cjs/root/card/getCardPreview.js +0 -173
  174. package/dist/cjs/utils/fileAttributesContext.js +0 -40
  175. package/dist/es2019/root/card/getCardPreview.js +0 -86
  176. package/dist/es2019/root/inline/inlineMediaCard.js +0 -92
  177. package/dist/es2019/utils/fileAttributesContext.js +0 -19
  178. package/dist/esm/root/card/getCardPreview.js +0 -153
  179. package/dist/esm/utils/fileAttributesContext.js +0 -18
  180. package/dist/types/root/card/getCardPreview.d.ts +0 -8
  181. package/dist/types/utils/fileAttributesContext.d.ts +0 -10
@@ -5,117 +5,188 @@ import { version as packageVersion, name as packageName } from '../../version.js
5
5
  import { withAnalyticsEvents } from '@atlaskit/analytics-next';
6
6
  import { withMediaAnalyticsContext } from '@atlaskit/media-common';
7
7
  import DownloadIcon from '@atlaskit/icon/glyph/download';
8
- import { isMimeTypeSupportedByBrowser } from '@atlaskit/media-common';
9
- import { addFileAttrsToUrl, globalMediaEventEmitter, isDifferentIdentifier, isFileIdentifier, isErrorFileState, RECENTS_COLLECTION, isPreviewableType } from '@atlaskit/media-client';
8
+ import { globalMediaEventEmitter, isDifferentIdentifier, isFileIdentifier, RECENTS_COLLECTION, isImageRepresentationReady, isExternalImageIdentifier, imageResizeModeToFileImageMode } from '@atlaskit/media-client';
10
9
  import { MediaViewer } from '@atlaskit/media-viewer';
11
10
  import { IntlProvider, intlShape } from 'react-intl';
12
11
  import { CardView } from '../cardView';
13
12
  import { ViewportDetector } from '../../utils/viewportDetector';
14
- import { getDataURIDimension } from '../../utils/getDataURIDimension';
15
- import { getCardPreviewFromFileState, getCardPreviewFromBackend } from './getCardPreview';
13
+ import { getRequestedDimensions } from '../../utils/getDataURIDimension';
14
+ import { getCardPreview, getCardPreviewFromCache, removeCardPreviewFromCache, getFilePreviewFromFileState, shouldResolvePreview } from './getCardPreview';
16
15
  import { getFileDetails } from '../../utils/metadata';
17
- import { isBigger } from '../../utils/dimensionComparer';
18
- import { createObjectURLCache } from '../../utils/objectURLCache';
19
- import { getCardStatus, extractCardStatusParams } from './getCardStatus';
20
16
  import { InlinePlayer } from '../inlinePlayer';
21
- import { fireMediaCardEvent, getFileAttributes, getRenderCommencedEventPayload, getCopiedFilePayload } from '../../utils/analytics';
22
- import { FileAttributesProvider } from '../../utils/fileAttributesContext';
23
- import { isLocalPreviewError, MediaCardError, ensureMediaCardError } from '../../errors';
24
- import { fireOperationalEvent, relevantFeatureFlagNames } from './cardAnalytics';
25
- const cardPreviewCache = createObjectURLCache();
17
+ import { getFileAttributes } from '../../utils/analytics';
18
+ import { isLocalPreviewError, MediaCardError, ensureMediaCardError, ImageLoadError } from '../../errors';
19
+ import { fireOperationalEvent, fireCommencedEvent, relevantFeatureFlagNames, fireCopiedEvent, fireScreenEvent } from './cardAnalytics';
20
+ import getDocument from '../../utils/document';
21
+ import { getCardStateFromFileState, createStateUpdater } from './cardState';
26
22
  export class CardBase extends Component {
23
+ // We initialise timeElapsedTillCommenced
24
+ // to avoid extra branching on a possibly undefined value.
27
25
  constructor(props) {
28
26
  super(props);
29
27
 
30
28
  _defineProperty(this, "hasBeenMounted", false);
31
29
 
32
- _defineProperty(this, "onCopyListener", () => {
33
- if (typeof window.getSelection === 'function') {
34
- const selection = window.getSelection();
35
- const {
36
- cardRef
37
- } = this.state;
38
-
39
- if (cardRef && selection && selection.containsNode && selection.containsNode(cardRef, true)) {
40
- this.fireFileCopiedEvent();
41
- }
42
- }
43
- });
44
-
45
- _defineProperty(this, "getPreviewCacheKey", (id, dimensions) => {
46
- // Dimensions are used to create a key.
47
- // Cache is invalidated when different dimensions are provided.
48
- return [id, dimensions.height, dimensions.width].join('-');
49
- });
30
+ _defineProperty(this, "timeElapsedTillCommenced", performance.now());
50
31
 
51
- _defineProperty(this, "shouldRefetchImage", (current, next) => {
52
- if (!current || !next) {
53
- return false;
54
- }
32
+ _defineProperty(this, "getImageURLParams", ({
33
+ collectionName: collection
34
+ }) => ({
35
+ collection,
36
+ mode: imageResizeModeToFileImageMode(this.props.resizeMode),
37
+ ...this.requestedDimensions,
38
+ allowAnimated: true
39
+ }));
55
40
 
56
- return isBigger(current, next);
41
+ _defineProperty(this, "getMediaBlobUrlAttrs", (identifier, fileState) => {
42
+ const {
43
+ id,
44
+ collectionName: collection
45
+ } = identifier;
46
+ const {
47
+ originalDimensions,
48
+ contextId,
49
+ alt
50
+ } = this.props;
51
+ const {
52
+ mimeType,
53
+ name,
54
+ size
55
+ } = getFileDetails(identifier, fileState);
56
+ return contextId ? {
57
+ id,
58
+ collection,
59
+ contextId,
60
+ mimeType,
61
+ name,
62
+ size,
63
+ ...(originalDimensions || this.requestedDimensions),
64
+ alt
65
+ } : undefined;
57
66
  });
58
67
 
59
- _defineProperty(this, "isLatestCardStatusUpdate", cardStatusUpdateTimestamp => !this.lastCardStatusUpdateTimestamp || this.lastCardStatusUpdateTimestamp <= cardStatusUpdateTimestamp);
60
-
61
- _defineProperty(this, "getCardPreview", async (mediaClient, identifier, fileState, metadata) => {
68
+ _defineProperty(this, "getCardPreviewParams", (identifier, fileState) => {
69
+ const {
70
+ isBannedLocalPreview
71
+ } = this.state;
72
+ const {
73
+ id
74
+ } = identifier;
62
75
  const {
63
76
  dimensions = {},
64
- originalDimensions,
65
- resizeMode,
66
- featureFlags
77
+ mediaClient
67
78
  } = this.props;
68
- const {
79
+ return {
80
+ mediaClient,
69
81
  id,
70
- collectionName
71
- } = identifier; // We aren't using the component state here, as cardPreviewCache has a shorter lifecycle
82
+ dimensions,
83
+ onLocalPreviewError: this.fireLocalPreviewErrorEvent,
84
+ filePreview: isBannedLocalPreview ? undefined : getFilePreviewFromFileState(fileState),
85
+ isRemotePreviewReady: isImageRepresentationReady(fileState),
86
+ imageUrlParams: this.getImageURLParams(identifier),
87
+ mediaBlobUrlAttrs: this.getMediaBlobUrlAttrs(identifier, fileState)
88
+ };
89
+ });
72
90
 
73
- const cacheKey = this.getPreviewCacheKey(id, dimensions);
91
+ _defineProperty(this, "resolvePreview", async (identifier, fileState) => {
92
+ try {
93
+ const params = this.getCardPreviewParams(identifier, fileState);
94
+ const cardPreview = await getCardPreview(params);
95
+ this.safeSetState({
96
+ cardPreview
97
+ });
98
+ } catch (e) {
99
+ const wrappedError = ensureMediaCardError('preview-fetch', e); // If remote preview fails, we set status 'error'
100
+ // If local preview fails (i.e, no remote preview available),
101
+ // we can stay in the same status until there is a remote preview available
102
+ // If it's any other error we set status 'error'
74
103
 
75
- if (cardPreviewCache.has(cacheKey)) {
76
- return cardPreviewCache.get(cacheKey);
77
- } // don't use error fileStates
104
+ if (isLocalPreviewError(wrappedError)) {
105
+ this.safeSetState({
106
+ isBannedLocalPreview: true
107
+ });
108
+ } else {
109
+ this.safeSetState({
110
+ status: 'error',
111
+ error: wrappedError
112
+ });
113
+ }
114
+ }
115
+ });
78
116
 
117
+ _defineProperty(this, "getPerformanceAttributes", () => {
118
+ const {
119
+ timeElapsedTillCommenced
120
+ } = this;
121
+ const timeElapsedTillEvent = performance.now();
122
+ const durationSinceCommenced = timeElapsedTillCommenced && timeElapsedTillEvent - timeElapsedTillCommenced;
123
+ return {
124
+ overall: {
125
+ durationSincePageStart: timeElapsedTillEvent,
126
+ durationSinceCommenced
127
+ }
128
+ };
129
+ });
79
130
 
80
- if (isErrorFileState(fileState)) {
81
- return;
131
+ _defineProperty(this, "onImageError", () => {
132
+ const {
133
+ cardPreview
134
+ } = this.state;
135
+ const error = new ImageLoadError(cardPreview === null || cardPreview === void 0 ? void 0 : cardPreview.source);
136
+
137
+ if (cardPreview !== null && cardPreview !== void 0 && cardPreview.source && ['local', 'cache-local'].includes(cardPreview.source)) {
138
+ const {
139
+ identifier,
140
+ dimensions = {}
141
+ } = this.props;
142
+ isFileIdentifier(identifier) && removeCardPreviewFromCache(identifier.id, dimensions);
143
+ this.safeSetState({
144
+ cardPreview: undefined,
145
+ isBannedLocalPreview: true
146
+ });
147
+ this.fireLocalPreviewErrorEvent(error);
148
+ } else {
149
+ this.safeSetState({
150
+ status: 'error',
151
+ error
152
+ });
82
153
  }
154
+ });
155
+
156
+ _defineProperty(this, "onImageLoad", () => {
157
+ this.safeSetState({
158
+ previewDidRender: true
159
+ });
160
+ });
83
161
 
162
+ _defineProperty(this, "fireCopiedEvent", () => {
84
163
  const {
85
- mediaType,
86
- mimeType
87
- } = fileState;
88
- const requestedDimensions = this.getRequestedDimensions(); // TODO: align these checks with helpers from Media Client
89
- // https://product-fabric.atlassian.net/browse/BMPT-1300
90
-
91
- const shouldUseLocalPreview = mediaType !== 'doc' && !!mimeType && isMimeTypeSupportedByBrowser(mimeType);
92
- const previewableType = isPreviewableType(mediaType, featureFlags);
93
- const cardPreview = shouldUseLocalPreview && (await getCardPreviewFromFileState(fileState)) || previewableType && (await getCardPreviewFromBackend(mediaClient, identifier, fileState, requestedDimensions, resizeMode));
94
-
95
- if (cardPreview) {
96
- if (cardPreview.dataURI) {
97
- // In case we've retrieved cardPreview using one of the two methods above,
98
- // we want to embed some meta context into dataURI for Copy/Paste to work.
99
- const contextDimensions = originalDimensions || requestedDimensions;
100
- cardPreview.dataURI = this.addContextToDataURI(cardPreview.dataURI, id, metadata, contextDimensions, collectionName);
101
- } // We store new cardPreview into cache
102
-
103
-
104
- cardPreviewCache.set(cacheKey, cardPreview);
105
- return cardPreview;
106
- }
164
+ createAnalyticsEvent
165
+ } = this.props;
166
+ const {
167
+ cardRef
168
+ } = this.state;
169
+ cardRef && createAnalyticsEvent && fireCopiedEvent(createAnalyticsEvent, this.metadata.id, cardRef);
107
170
  });
108
171
 
109
- _defineProperty(this, "fireFileCopiedEvent", () => {
172
+ _defineProperty(this, "fireScreenEvent", () => {
110
173
  const {
111
174
  createAnalyticsEvent
112
175
  } = this.props;
113
- fireMediaCardEvent(getCopiedFilePayload(this.metadata.id), createAnalyticsEvent);
176
+ createAnalyticsEvent && fireScreenEvent(createAnalyticsEvent, this.fileAttributes);
177
+ });
178
+
179
+ _defineProperty(this, "fireLocalPreviewErrorEvent", error => {// TODO: track local preview success rate
180
+ // https://product-fabric.atlassian.net/browse/MEX-774
114
181
  });
115
182
 
116
183
  _defineProperty(this, "safeSetState", state => {
117
184
  if (this.hasBeenMounted) {
118
- this.setState(state);
185
+ // If it's setting the status, we need to use a state updater function,
186
+ // which ensures that no non-final status overrides a final status.
187
+ // If no status is set, we don't need a sate updater
188
+ const updater = !!state.status ? createStateUpdater(state) : state;
189
+ this.setState(updater);
119
190
  }
120
191
  });
121
192
 
@@ -125,8 +196,9 @@ export class CardBase extends Component {
125
196
  }
126
197
 
127
198
  if (this.hasBeenMounted) {
199
+ // TODO MEX-788: add test for "do not remove the card preview when unsubscribing".
128
200
  this.setState({
129
- cardPreview: undefined
201
+ isBannedLocalPreview: false
130
202
  });
131
203
  }
132
204
  });
@@ -317,7 +389,7 @@ export class CardBase extends Component {
317
389
  } = this;
318
390
  const card = /*#__PURE__*/React.createElement(CardView, {
319
391
  status: status,
320
- error: error && error.secondaryError,
392
+ error: error,
321
393
  mediaItemType: mediaItemType,
322
394
  metadata: metadata,
323
395
  dataURI: dataURI,
@@ -338,7 +410,9 @@ export class CardBase extends Component {
338
410
  testId: testId,
339
411
  featureFlags: featureFlags,
340
412
  titleBoxBgColor: titleBoxBgColor,
341
- titleBoxIcon: titleBoxIcon
413
+ titleBoxIcon: titleBoxIcon,
414
+ onImageError: this.onImageError,
415
+ onImageLoad: this.onImageLoad
342
416
  });
343
417
  return isLazy ? /*#__PURE__*/React.createElement(ViewportDetector, {
344
418
  targetRef: cardRef,
@@ -386,6 +460,8 @@ export class CardBase extends Component {
386
460
  }
387
461
  });
388
462
 
463
+ let _status = 'loading';
464
+
389
465
  let _cardPreview;
390
466
 
391
467
  const {
@@ -397,33 +473,52 @@ export class CardBase extends Component {
397
473
  const {
398
474
  id
399
475
  } = _identifier;
400
- const cacheKey = this.getPreviewCacheKey(id, _dimensions);
401
- _cardPreview = cardPreviewCache.get(cacheKey);
402
- }
403
- /**
404
- * If cardPreview is available from local cache, `isCardVisible`
405
- * should be true to avoid flickers during re-mount of the component
406
- */
476
+ _cardPreview = getCardPreviewFromCache(id, _dimensions);
477
+ } else if (isExternalImageIdentifier(_identifier)) {
478
+ this.fireCommencedEvent();
479
+ _status = 'loading-preview';
480
+ const {
481
+ dataURI
482
+ } = _identifier;
483
+ _cardPreview = {
484
+ dataURI,
485
+ orientation: 1,
486
+ source: 'external'
487
+ };
488
+ } // If cardPreview is available from local cache, `isCardVisible`
489
+ // should be true to avoid flickers during re-mount of the component
407
490
 
408
491
 
492
+ const isCardVisible = _cardPreview ? true : !this.props.isLazy;
409
493
  this.state = {
410
- status: 'loading',
411
- isCardVisible: _cardPreview ? true : !this.props.isLazy,
494
+ status: _status,
495
+ isCardVisible,
412
496
  isPlayingFile: false,
413
497
  cardPreview: _cardPreview,
414
- cardRef: null
498
+ cardRef: null,
499
+ isBannedLocalPreview: false,
500
+ previewDidRender: false
415
501
  };
416
- } // we add a listener for each of the cards on the page
417
- // and then check if the triggered listener is from the card
418
- // that contains a div in current window.getSelection()
419
- // won't work in IE11
420
-
502
+ }
421
503
 
422
504
  componentDidMount() {
423
505
  this.hasBeenMounted = true;
424
- this.fireCommencedEvent();
425
- this.updateStateForIdentifier();
426
- document.addEventListener('copy', this.onCopyListener);
506
+ const {
507
+ isCardVisible
508
+ } = this.state;
509
+ const {
510
+ identifier
511
+ } = this.props;
512
+
513
+ if (isCardVisible && isFileIdentifier(identifier)) {
514
+ this.updateStateForIdentifier(identifier);
515
+ } // we add a listener for each of the cards on the page
516
+ // and then check if the triggered listener is from the card
517
+ // that contains a div in current window.getSelection()
518
+ // won't work in IE11
519
+
520
+
521
+ getDocument().addEventListener('copy', this.fireCopiedEvent);
427
522
  }
428
523
 
429
524
  componentDidUpdate(prevProps, prevState) {
@@ -438,215 +533,162 @@ export class CardBase extends Component {
438
533
  const {
439
534
  mediaClient,
440
535
  identifier,
441
- dimensions
536
+ dimensions,
537
+ featureFlags
442
538
  } = this.props;
443
539
  const {
444
- isCardVisible
540
+ isCardVisible,
541
+ cardPreview,
542
+ status,
543
+ fileState,
544
+ isBannedLocalPreview,
545
+ previewDidRender
445
546
  } = this.state;
446
547
  const isDifferent = isDifferentIdentifier(prevIdentifier, identifier);
548
+ const turnedVisible = !prevIsCardVisible && isCardVisible;
549
+ const isNewMediaClient = prevMediaClient !== mediaClient;
447
550
 
448
- if (prevIsCardVisible !== isCardVisible && isCardVisible || prevMediaClient !== mediaClient || isDifferent || this.shouldRefetchImage(prevDimensions, dimensions)) {
551
+ if (isExternalImageIdentifier(identifier) && isDifferent) {
449
552
  this.fireCommencedEvent();
450
- this.updateStateForIdentifier();
553
+ const {
554
+ dataURI
555
+ } = identifier;
556
+ this.setState({
557
+ status: 'loading-preview',
558
+ cardPreview: {
559
+ dataURI,
560
+ orientation: 1,
561
+ source: 'external'
562
+ },
563
+ isCardVisible: true
564
+ });
565
+ }
566
+
567
+ if (isFileIdentifier(identifier) && (turnedVisible || !!this.subscription && (isNewMediaClient || isDifferent))) {
568
+ this.updateStateForIdentifier(identifier);
451
569
  }
452
570
 
453
571
  if (this.state.status !== prevState.status) {
454
- const {
455
- status,
456
- cardPreview,
457
- error
458
- } = this.state;
459
- const {
460
- createAnalyticsEvent
461
- } = this.props;
462
- createAnalyticsEvent && fireOperationalEvent(createAnalyticsEvent, status, this.fileAttributes, {
463
- cardPreview,
464
- error
572
+ this.fireOperationalEvent();
573
+
574
+ if (this.state.status === 'complete' || this.fileAttributes.fileMediatype === 'video' && !!cardPreview && this.state.status === 'processing') {
575
+ this.fireScreenEvent();
576
+ }
577
+ }
578
+
579
+ if (isFileIdentifier(identifier) && fileState && shouldResolvePreview({
580
+ status,
581
+ fileState,
582
+ dimensions,
583
+ prevDimensions,
584
+ featureFlags,
585
+ hasCardPreview: !!cardPreview,
586
+ isBannedLocalPreview
587
+ })) {
588
+ this.resolvePreview(identifier, fileState);
589
+ }
590
+
591
+ if (previewDidRender && // We should't complete if status is uploading
592
+ ['loading', 'loading-preview', 'processing'].includes(status)) {
593
+ this.safeSetState({
594
+ status: 'complete'
465
595
  });
596
+ this.unsubscribe();
466
597
  }
467
598
  }
468
599
 
469
600
  componentWillUnmount() {
470
601
  this.hasBeenMounted = false;
471
602
  this.unsubscribe();
472
- document.removeEventListener('copy', this.onCopyListener);
603
+ getDocument().removeEventListener('copy', this.fireCopiedEvent);
473
604
  }
474
605
 
475
- updateStateForIdentifier() {
476
- const {
477
- mediaClient,
478
- identifier
479
- } = this.props;
480
- const {
481
- isCardVisible
482
- } = this.state;
483
-
484
- if (!isCardVisible) {
485
- return;
486
- }
487
-
488
- if (identifier.mediaItemType === 'file') {
489
- this.unsubscribe();
490
- this.subscribeInternalFile(identifier, mediaClient);
491
- }
606
+ updateStateForIdentifier(identifier) {
607
+ this.fireCommencedEvent();
608
+ this.subscribeInternalFile(identifier);
492
609
  }
493
610
 
494
- getRequestedDimensions() {
611
+ subscribeInternalFile(identifier) {
495
612
  const {
496
- dimensions
613
+ mediaClient
497
614
  } = this.props;
498
615
  const {
499
- cardRef
616
+ isBannedLocalPreview
500
617
  } = this.state;
501
- const options = {
502
- dimensions,
503
- element: cardRef
504
- };
505
- const width = getDataURIDimension('width', options);
506
- const height = getDataURIDimension('height', options);
507
- return {
508
- width,
509
- height
510
- };
511
- }
512
-
513
- addContextToDataURI(dataURI, fileId, metadata, {
514
- width,
515
- height
516
- }, collectionName) {
517
- const {
518
- contextId,
519
- alt
520
- } = this.props;
521
-
522
- if (!contextId) {
523
- return dataURI;
524
- }
525
-
526
- return addFileAttrsToUrl(dataURI, {
527
- id: fileId,
528
- collection: collectionName,
529
- contextId,
530
- mimeType: metadata.mimeType,
531
- name: metadata.name,
532
- size: metadata.size,
533
- width,
534
- height,
535
- alt
536
- });
537
- }
538
-
539
- subscribeInternalFile(identifier, mediaClient) {
540
618
  const {
541
619
  id,
542
620
  collectionName,
543
621
  occurrenceKey
544
622
  } = identifier;
623
+ this.unsubscribe();
545
624
  this.subscription = mediaClient.file.getFileState(id, {
546
625
  collectionName,
547
626
  occurrenceKey
548
627
  }).subscribe({
549
- next: async fileState => {
550
- this.lastFileState = fileState;
551
- const thisCardStatusUpdateTimestamp = (performance || Date).now();
552
- let status = getCardStatus(fileState.status, extractCardStatusParams(fileState, this.props.featureFlags));
553
- this.safeSetState({
554
- fileState
555
- });
556
- let cardPreview;
557
- let error;
558
-
559
- try {
560
- cardPreview = await this.getCardPreview(mediaClient, identifier, fileState, getFileDetails(fileState));
561
- } catch (e) {
562
- const wrappedError = ensureMediaCardError('preview-fetch', e); // If the local preview fails, we don't need to display the error.
563
- // TODO: if file has local preview card will be complete status. But if the local preview fails,
564
- // and we don't change the status, we will have
565
- // [ !cardPreview && status == complete -> log succeeded event ]
566
- // https://product-fabric.atlassian.net/browse/BMPT-1315
567
-
568
- if (!isLocalPreviewError(wrappedError)) {
569
- status = 'error';
570
- error = wrappedError;
571
- }
572
- }
573
-
574
- if (this.isLatestCardStatusUpdate(thisCardStatusUpdateTimestamp)) {
575
- // These status and progress must not override values representing more recent FileState
576
-
577
- /* next() start some await() delay in next() status & progress update
578
- * ------- ------------------ ------------------------
579
- * |----[1]FileState:uploading------>| |
580
- * | | |
581
- * |----[2]FileState:uploading------>| |
582
- * | | |
583
- * | |----[2]FileState:uploading------>| Update status to `uploading`
584
- * |----[3]FileState:processing----->| |
585
- * | |----[3]FileState:processing----->| Update status to `complete`
586
- * | | |
587
- * | |----[1]FileState:uploading------>| We do not want to update status to `uploading` again!
588
- *
589
- */
590
- if (status === 'error' && isErrorFileState(fileState) && !error) {
591
- error = new MediaCardError('error-file-state', new Error(fileState.message));
592
- }
593
-
594
- this.safeSetState({
595
- status,
596
- cardPreview,
597
- progress: status === 'uploading' && fileState.status === 'uploading' ? fileState.progress : 1,
598
- error: status === 'error' && error ? error : undefined
599
- });
600
- this.lastCardStatusUpdateTimestamp = thisCardStatusUpdateTimestamp;
601
- }
628
+ next: fileState => {
629
+ const {
630
+ featureFlags
631
+ } = this.props;
632
+ const newState = getCardStateFromFileState(fileState, isBannedLocalPreview, featureFlags);
633
+ this.safeSetState(newState);
602
634
  },
603
635
  error: e => {
604
- // If file state subscription decides that the card is complete
605
- // and later there is an error, we won't change the card's status.
606
- if (this.state.status === 'complete') {
607
- return;
608
- }
609
-
610
- const errorReason = this.fileAttributes.fileStatus === 'uploading' ? 'upload' : 'metadata-fetch';
636
+ const errorReason = this.state.status === 'uploading' ? 'upload' : 'metadata-fetch';
611
637
  const error = new MediaCardError(errorReason, e);
612
638
  this.safeSetState({
613
639
  error,
614
640
  status: 'error'
615
641
  });
616
- this.lastCardStatusUpdateTimestamp = (performance || Date).now();
617
642
  }
618
643
  });
619
644
  }
620
645
 
621
- get metadata() {
646
+ get requestedDimensions() {
622
647
  const {
623
- identifier
648
+ dimensions
624
649
  } = this.props;
625
- return isFileIdentifier(identifier) ? this.state.fileState ? getFileDetails(this.state.fileState) : {
626
- id: identifier.id
627
- } : {
628
- id: identifier.mediaItemType,
629
- name: identifier.name || identifier.dataURI,
630
- mediaType: 'image'
631
- };
650
+ const {
651
+ cardRef: element
652
+ } = this.state;
653
+ return getRequestedDimensions({
654
+ dimensions,
655
+ element
656
+ });
657
+ }
658
+
659
+ get metadata() {
660
+ var _this$state;
661
+
662
+ return getFileDetails(this.props.identifier, (_this$state = this.state) === null || _this$state === void 0 ? void 0 : _this$state.fileState);
632
663
  }
633
664
 
634
665
  get fileAttributes() {
635
- var _this$lastFileState;
666
+ var _this$state2, _this$state2$fileStat;
636
667
 
637
- return getFileAttributes(this.metadata, (_this$lastFileState = this.lastFileState) === null || _this$lastFileState === void 0 ? void 0 : _this$lastFileState.status);
668
+ return getFileAttributes(this.metadata, (_this$state2 = this.state) === null || _this$state2 === void 0 ? void 0 : (_this$state2$fileStat = _this$state2.fileState) === null || _this$state2$fileStat === void 0 ? void 0 : _this$state2$fileStat.status);
638
669
  }
639
670
 
640
- fireCommencedEvent() {
641
- if (!this.state.isCardVisible) {
642
- return;
643
- }
671
+ fireOperationalEvent() {
672
+ const {
673
+ status,
674
+ error
675
+ } = this.state;
676
+ const {
677
+ createAnalyticsEvent
678
+ } = this.props;
679
+ createAnalyticsEvent && fireOperationalEvent(createAnalyticsEvent, status, this.fileAttributes, this.getPerformanceAttributes(), error);
680
+ }
644
681
 
682
+ fireCommencedEvent() {
683
+ this.timeElapsedTillCommenced = performance.now();
645
684
  const {
646
685
  createAnalyticsEvent
647
686
  } = this.props;
648
- const payload = getRenderCommencedEventPayload(this.fileAttributes);
649
- fireMediaCardEvent(payload, createAnalyticsEvent);
687
+ createAnalyticsEvent && fireCommencedEvent(createAnalyticsEvent, this.fileAttributes, {
688
+ overall: {
689
+ durationSincePageStart: this.timeElapsedTillCommenced
690
+ }
691
+ });
650
692
  }
651
693
 
652
694
  get actions() {
@@ -677,19 +719,14 @@ export class CardBase extends Component {
677
719
  }
678
720
 
679
721
  render() {
680
- var _this$lastFileState2;
681
-
682
722
  const {
683
723
  isPlayingFile,
684
724
  mediaViewerSelectedItem
685
725
  } = this.state;
686
726
  const innerContent = /*#__PURE__*/React.createElement(React.Fragment, null, isPlayingFile ? this.renderInlinePlayer() : this.renderCard(), mediaViewerSelectedItem ? this.renderMediaViewer() : null);
687
- const content = this.context.intl ? innerContent : /*#__PURE__*/React.createElement(IntlProvider, {
727
+ return this.context.intl ? innerContent : /*#__PURE__*/React.createElement(IntlProvider, {
688
728
  locale: "en"
689
729
  }, innerContent);
690
- return /*#__PURE__*/React.createElement(FileAttributesProvider, {
691
- data: getFileAttributes(this.metadata, (_this$lastFileState2 = this.lastFileState) === null || _this$lastFileState2 === void 0 ? void 0 : _this$lastFileState2.status)
692
- }, content);
693
730
  }
694
731
 
695
732
  }
@@ -708,27 +745,6 @@ _defineProperty(CardBase, "contextTypes", {
708
745
  intl: intlShape
709
746
  });
710
747
 
711
- _defineProperty(CardBase, "getDerivedStateFromProps", props => {
712
- const {
713
- identifier
714
- } = props;
715
-
716
- if (identifier.mediaItemType === 'external-image') {
717
- const {
718
- dataURI
719
- } = identifier;
720
- return {
721
- status: 'complete',
722
- cardPreview: {
723
- dataURI,
724
- orientation: 1
725
- }
726
- };
727
- }
728
-
729
- return null;
730
- });
731
-
732
748
  export const Card = withMediaAnalyticsContext({
733
749
  packageVersion,
734
750
  packageName,