@atlaskit/media-card 77.4.4 → 77.4.6

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 (31) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/cjs/card/card.js +42 -38
  3. package/dist/cjs/card/getCardPreview/filePreviewStatus.js +2 -0
  4. package/dist/cjs/card/media-card-analytics-error-boundary.js +1 -1
  5. package/dist/cjs/card/v2/cardV2.js +1 -1
  6. package/dist/cjs/card/v2/fileCard.js +120 -521
  7. package/dist/cjs/card/v2/useFilePreview.js +592 -0
  8. package/dist/cjs/inline/loader.js +1 -1
  9. package/dist/cjs/utils/ufoExperiences.js +1 -1
  10. package/dist/es2019/card/card.js +7 -4
  11. package/dist/es2019/card/getCardPreview/filePreviewStatus.js +2 -0
  12. package/dist/es2019/card/media-card-analytics-error-boundary.js +1 -1
  13. package/dist/es2019/card/v2/cardV2.js +1 -1
  14. package/dist/es2019/card/v2/fileCard.js +105 -436
  15. package/dist/es2019/card/v2/useFilePreview.js +499 -0
  16. package/dist/es2019/inline/loader.js +1 -1
  17. package/dist/es2019/utils/ufoExperiences.js +1 -1
  18. package/dist/esm/card/card.js +42 -38
  19. package/dist/esm/card/getCardPreview/filePreviewStatus.js +2 -0
  20. package/dist/esm/card/media-card-analytics-error-boundary.js +1 -1
  21. package/dist/esm/card/v2/cardV2.js +1 -1
  22. package/dist/esm/card/v2/fileCard.js +125 -526
  23. package/dist/esm/card/v2/useFilePreview.js +584 -0
  24. package/dist/esm/inline/loader.js +1 -1
  25. package/dist/esm/utils/ufoExperiences.js +1 -1
  26. package/dist/types/card/card.d.ts +1 -1
  27. package/dist/types/card/v2/useFilePreview.d.ts +38 -0
  28. package/dist/types-ts4.5/card/card.d.ts +1 -1
  29. package/dist/types-ts4.5/card/v2/useFilePreview.d.ts +38 -0
  30. package/package.json +4 -4
  31. package/tmp/api-report-tmp.d.ts +0 -345
@@ -1,31 +1,26 @@
1
1
  import DownloadIcon from '@atlaskit/icon/glyph/download';
2
- import { RECENTS_COLLECTION, addFileAttrsToUrl, globalMediaEventEmitter, imageResizeModeToFileImageMode, isFileIdentifier, isImageRepresentationReady } from '@atlaskit/media-client';
2
+ import { RECENTS_COLLECTION, globalMediaEventEmitter, isImageRepresentationReady } from '@atlaskit/media-client';
3
3
  import { MediaFileStateError, useFileState } from '@atlaskit/media-client-react';
4
- import { getMediaTypeFromMimeType, getRandomHex, isMimeTypeSupportedByBrowser } from '@atlaskit/media-common';
5
- import { getOrientation } from '@atlaskit/media-ui';
4
+ import { getRandomHex, isMimeTypeSupportedByBrowser } from '@atlaskit/media-common';
6
5
  import { MediaViewer } from '@atlaskit/media-viewer';
7
- import React, { Suspense, useCallback, useEffect, useMemo, useRef, useState } from 'react';
6
+ import React, { Suspense, useEffect, useMemo, useRef, useState } from 'react';
8
7
  import ReactDOM from 'react-dom';
9
- import { ImageLoadError, LocalPreviewError, MediaCardError, ensureMediaCardError, isLocalPreviewError, isUnsupportedLocalPreviewError } from '../../errors';
10
- import { extractErrorInfo } from '../../utils/analytics';
11
- import { isBigger } from '../../utils/dimensionComparer';
8
+ import { MediaCardError } from '../../errors';
12
9
  import getDocument from '../../utils/document';
13
10
  import { generateUniqueId } from '../../utils/generateUniqueId';
14
11
  import { getRequestedDimensions } from '../../utils/getDataURIDimension';
15
12
  import { getMediaCardCursor } from '../../utils/getMediaCardCursor';
16
- import { getSSRData, StoreSSRDataScript } from '../../utils/globalScope';
17
13
  import { getFileDetails } from '../../utils/metadata';
18
14
  import { abortUfoExperience, completeUfoExperience, startUfoExperience } from '../../utils/ufoExperiences';
19
15
  import { useCurrentValueRef } from '../../utils/useCurrentValueRef';
20
16
  import { usePrevious } from '../../utils/usePrevious';
21
17
  import { videoIsPlayable } from '../../utils/videoIsPlayable';
22
- import { takeSnapshot } from '../../utils/videoSnapshot';
23
18
  import { ViewportDetector } from '../../utils/viewportDetector';
24
19
  import { fireCommencedEvent, fireCopiedEvent, fireNonCriticalErrorEvent, fireOperationalEvent, fireScreenEvent } from '../cardAnalytics';
25
- import { fetchAndCacheRemotePreview, getCardPreviewFromCache, getSSRCardPreview, isLocalPreview, isSSRClientPreview, isSSRDataPreview, isSSRPreview, removeCardPreviewFromCache, shouldResolvePreview } from '../getCardPreview';
26
- import cardPreviewCache from '../getCardPreview/cache';
20
+ import { isSSRPreview } from '../getCardPreview';
27
21
  import { CardViewV2 } from './cardViewV2';
28
22
  import { InlinePlayerLazyV2 } from './inlinePlayerLazyV2';
23
+ import { useFilePreview } from './useFilePreview';
29
24
  export const FileCard = ({
30
25
  appearance = 'auto',
31
26
  resizeMode = 'crop',
@@ -56,98 +51,18 @@ export const FileCard = ({
56
51
  onMouseEnter,
57
52
  createAnalyticsEvent
58
53
  }) => {
59
- var _ssrReliabilityRef$cu;
60
54
  //----------------------------------------------------------------//
61
- //---------------- State Initializer Functions -------------------//
55
+ //------------ State, Refs & Initial Values ----------------------//
62
56
  //----------------------------------------------------------------//
63
57
 
64
- const ssrDataRef = useRef();
65
- const ssrReliabilityRef = useRef({
66
- server: {
67
- status: 'unknown'
68
- },
69
- client: {
70
- status: 'unknown'
71
- }
72
- });
73
58
  const [cardElement, setCardElement] = useState(null);
74
- const imageURLParams = useMemo(() => ({
75
- collection: identifier.collectionName,
76
- mode: resizeMode === 'stretchy-fit' ? 'full-fit' : resizeMode,
77
- ...getRequestedDimensions({
78
- dimensions,
79
- element: cardElement
80
- }),
81
- allowAnimated: true
82
- }), [cardElement, dimensions, identifier.collectionName, resizeMode]);
83
- const getMediaBlobUrlAttrs = useCallback(fileState => {
84
- const {
85
- id,
86
- collectionName: collection
87
- } = identifier;
88
- const {
89
- mimeType,
90
- name,
91
- size
92
- } = getFileDetails(identifier, fileState);
93
- return contextId ? {
94
- id,
95
- collection,
96
- contextId,
97
- mimeType,
98
- name,
99
- size,
100
- ...(originalDimensions || getRequestedDimensions({
101
- dimensions,
102
- element: cardElement
103
- })),
104
- alt
105
- } : undefined;
106
- }, [alt, cardElement, contextId, dimensions, identifier, originalDimensions]);
107
- const getSSRPreview = (ssr, identifier, mediaClient) => {
108
- var _ssrDataRef$current, _ssrDataRef$current2;
109
- ssrDataRef.current = getSSRData(identifier);
110
- if ((_ssrDataRef$current = ssrDataRef.current) !== null && _ssrDataRef$current !== void 0 && _ssrDataRef$current.error) {
111
- ssrReliabilityRef.current.server = {
112
- status: 'fail',
113
- ...ssrDataRef.current.error
114
- };
115
- }
116
- if (!((_ssrDataRef$current2 = ssrDataRef.current) !== null && _ssrDataRef$current2 !== void 0 && _ssrDataRef$current2.dataURI)) {
117
- try {
118
- return getSSRCardPreview(ssr, mediaClient, identifier.id, imageURLParams, getMediaBlobUrlAttrs(fileStateValue));
119
- } catch (e) {
120
- ssrReliabilityRef.current[ssr] = {
121
- status: 'fail',
122
- ...extractErrorInfo(e)
123
- };
124
- }
125
- } else {
126
- return {
127
- dataURI: ssrDataRef.current.dataURI,
128
- source: 'ssr-data'
129
- };
130
- }
131
- };
132
- const cardPreviewInitializer = () => {
133
- let cardPreview;
134
- const {
135
- id
136
- } = identifier;
137
- const fileImageMode = imageResizeModeToFileImageMode(resizeMode);
138
- cardPreview = getCardPreviewFromCache(id, fileImageMode);
139
- if (!cardPreview && ssr) {
140
- cardPreview = getSSRPreview(ssr, identifier, mediaClient);
141
- }
142
- return cardPreview;
143
- };
144
- const [cardPreview, setCardPreview] = useState(cardPreviewInitializer);
145
-
146
- // If cardPreview is available from local cache or external, `isCardVisible`
147
- // should be true to avoid flickers during re-mount of the component
148
- // should not be visible for SSR preview, otherwise we'll fire the metadata fetch from
149
- // outside the viewport
150
- const [isCardVisible, setIsCardVisible] = useState(() => !isLazy || cardPreview && !isSSRPreview(cardPreview));
59
+ const requestedDimensions = useMemo(() =>
60
+ // requested dimensions is eventually an expensive operation if the dimensions are a percentage
61
+ getRequestedDimensions({
62
+ dimensions,
63
+ element: cardElement
64
+ }), [dimensions, cardElement]);
65
+ const [isCardVisible, setIsCardVisible] = useState(!isLazy);
151
66
  const {
152
67
  fileState
153
68
  } = useFileState(identifier.id, {
@@ -182,10 +97,58 @@ export const FileCard = ({
182
97
  const [status, setStatus] = useState('loading');
183
98
  const [isPlayingFile, setIsPlayingFile] = useState(false);
184
99
  const [shouldAutoplay, setShouldAutoplay] = useState(false);
100
+
101
+ // CXP-2723 TODO: remove isBannedLocalPreview
185
102
  const [isBannedLocalPreview, setIsBannedLocalPreview] = useState(false);
186
103
  const [previewDidRender, setPreviewDidRender] = useState(false);
104
+
105
+ // CXP-2723 TODO: Do we have tests to validate the use of the below attributes
106
+ const mediaBlobUrlAttrs = useMemo(() => {
107
+ const {
108
+ id,
109
+ collectionName: collection
110
+ } = identifier;
111
+ const {
112
+ mimeType,
113
+ name,
114
+ size
115
+ } = getFileDetails(identifier, fileStateValue);
116
+ return contextId ? {
117
+ id,
118
+ collection,
119
+ contextId,
120
+ mimeType,
121
+ name,
122
+ size,
123
+ ...(originalDimensions || requestedDimensions),
124
+ alt
125
+ } : undefined;
126
+ }, [alt, requestedDimensions, contextId, fileStateValue, identifier, originalDimensions]);
127
+ const {
128
+ cardPreview,
129
+ error: previewError,
130
+ nonCriticalError,
131
+ ssrReliabilityRef,
132
+ onImageError: onImageErrorBase,
133
+ onImageLoad: onImageLoadBase,
134
+ StoreSSRDataScript
135
+ } = useFilePreview({
136
+ mediaBlobUrlAttrs,
137
+ resizeMode,
138
+ identifier,
139
+ ssr,
140
+ mediaClient,
141
+ dimensions,
142
+ requestedDimensions,
143
+ traceContext,
144
+ previewDidRender,
145
+ skipRemote: !isCardVisible
146
+ });
187
147
  const [error, setError] = useState();
188
- const wasResolvedUpfrontPreviewRef = useRef(false);
148
+
149
+ // CXP-2723 TODO: TEMPORARY VARIABLES
150
+ const finalError = error || previewError;
151
+ const finalStatus = finalError ? 'error' : status;
189
152
  const [mediaViewerSelectedItem, setMediaViewerSelectedItem] = useState(null);
190
153
  const uploadProgressRef = useRef();
191
154
  const metadata = useMemo(() => {
@@ -225,7 +188,7 @@ export const FileCard = ({
225
188
  };
226
189
  }, [fileStateValue === null || fileStateValue === void 0 ? void 0 : fileStateValue.status, metadata.id, metadata.mediaType, metadata.mimeType, metadata.size]);
227
190
  const computedActions = useMemo(() => {
228
- if (status === 'failed-processing' || shouldEnableDownloadButton) {
191
+ if (finalStatus === 'failed-processing' || shouldEnableDownloadButton) {
229
192
  const downloadAction = {
230
193
  label: 'Download',
231
194
  icon: /*#__PURE__*/React.createElement(DownloadIcon, {
@@ -237,7 +200,7 @@ export const FileCard = ({
237
200
  } else {
238
201
  return actions;
239
202
  }
240
- }, [actions, identifier.collectionName, identifier.id, mediaClient.file, metadata.name, shouldEnableDownloadButton, status]);
203
+ }, [actions, identifier.collectionName, identifier.id, mediaClient.file, metadata.name, shouldEnableDownloadButton, finalStatus]);
241
204
 
242
205
  //----------------------------------------------------------------//
243
206
  //---------------------- Analytics ------------------------------//
@@ -252,12 +215,17 @@ export const FileCard = ({
252
215
  durationSinceCommenced
253
216
  }
254
217
  };
255
- createAnalyticsEvent && fireOperationalEvent(createAnalyticsEvent, status, fileAttributes, performanceAttributes, ssrReliabilityRef.current, error, traceContext, fileStateValue === null || fileStateValue === void 0 ? void 0 : fileStateValue.metadataTraceContext);
256
- completeUfoExperience(internalOccurrenceKey, status, fileAttributes, fileStateFlagsRef.current, ssrReliabilityRef.current, error);
218
+ createAnalyticsEvent && fireOperationalEvent(createAnalyticsEvent, finalStatus, fileAttributes, performanceAttributes, ssrReliabilityRef.current, finalError, traceContext, fileStateValue === null || fileStateValue === void 0 ? void 0 : fileStateValue.metadataTraceContext);
219
+ completeUfoExperience(internalOccurrenceKey, finalStatus, fileAttributes, fileStateFlagsRef.current, ssrReliabilityRef.current, finalError);
257
220
  });
258
221
  const fireNonCriticalErrorEventRef = useCurrentValueRef(error => {
259
- createAnalyticsEvent && fireNonCriticalErrorEvent(createAnalyticsEvent, status, fileAttributes, ssrReliabilityRef.current, error, traceContext, fileStateValue === null || fileStateValue === void 0 ? void 0 : fileStateValue.metadataTraceContext);
222
+ createAnalyticsEvent && fireNonCriticalErrorEvent(createAnalyticsEvent, finalStatus, fileAttributes, ssrReliabilityRef.current, error, traceContext, fileStateValue === null || fileStateValue === void 0 ? void 0 : fileStateValue.metadataTraceContext);
260
223
  });
224
+ useEffect(() => {
225
+ if (nonCriticalError) {
226
+ fireNonCriticalErrorEventRef.current(nonCriticalError);
227
+ }
228
+ }, [nonCriticalError, fireNonCriticalErrorEventRef]);
261
229
  const fireScreenEventRef = useCurrentValueRef(() => {
262
230
  createAnalyticsEvent && fireScreenEvent(createAnalyticsEvent, fileAttributes);
263
231
  });
@@ -283,73 +251,10 @@ export const FileCard = ({
283
251
  //----------------------------------------------------------------//
284
252
 
285
253
  const onImageError = newCardPreview => {
286
- if (newCardPreview) {
287
- const failedSSRObject = {
288
- status: 'fail',
289
- ...extractErrorInfo(new ImageLoadError(newCardPreview.source))
290
- };
291
- if (isSSRClientPreview(newCardPreview)) {
292
- ssrReliabilityRef.current.client = failedSSRObject;
293
- }
294
-
295
- /*
296
- If the cardPreview failed and it comes from server (global scope / ssrData), it means that we have reused it in client and the error counts for both: server & client.
297
- */
298
-
299
- if (isSSRDataPreview(newCardPreview)) {
300
- ssrReliabilityRef.current.server = failedSSRObject;
301
- ssrReliabilityRef.current.client = failedSSRObject;
302
- }
303
- }
304
-
305
- // If the dataURI has been replaced, we can dismiss this error
306
- if ((newCardPreview === null || newCardPreview === void 0 ? void 0 : newCardPreview.dataURI) !== (cardPreview === null || cardPreview === void 0 ? void 0 : cardPreview.dataURI)) {
307
- return;
308
- }
309
- const error = new ImageLoadError(newCardPreview === null || newCardPreview === void 0 ? void 0 : newCardPreview.source);
310
- const isLocal = newCardPreview && isLocalPreview(newCardPreview);
311
- const isSSR = newCardPreview && (isSSRClientPreview(newCardPreview) || isSSRDataPreview(newCardPreview));
312
- if (isLocal || isSSR) {
313
- if (isLocal) {
314
- setIsBannedLocalPreview(true);
315
- fireNonCriticalErrorEventRef.current && fireNonCriticalErrorEventRef.current(error);
316
- }
317
- const fileImageMode = imageResizeModeToFileImageMode(resizeMode);
318
- isFileIdentifier(identifier) && removeCardPreviewFromCache(identifier.id, fileImageMode);
319
- setCardPreview(undefined);
320
- } else {
321
- if (!['complete', 'error', 'failed-processing'].includes(status)) {
322
- setStatus('error');
323
- setError(error);
324
- }
325
- }
254
+ onImageErrorBase(newCardPreview);
326
255
  };
327
256
  const onImageLoad = newCardPreview => {
328
- if (newCardPreview) {
329
- if (isSSRClientPreview(newCardPreview) && ssrReliabilityRef.current.client.status === 'unknown') {
330
- ssrReliabilityRef.current.client = {
331
- status: 'success'
332
- };
333
- }
334
-
335
- /*
336
- If the image loads successfully and it comes from server (global scope / ssrData), it means that we have reused it in client and the success counts for both: server & client.
337
- */
338
-
339
- if (isSSRDataPreview(newCardPreview) && ssrReliabilityRef.current.server.status === 'unknown') {
340
- ssrReliabilityRef.current.server = {
341
- status: 'success'
342
- };
343
- ssrReliabilityRef.current.client = {
344
- status: 'success'
345
- };
346
- }
347
- }
348
-
349
- // If the dataURI has been replaced, we can dismiss this callback
350
- if ((newCardPreview === null || newCardPreview === void 0 ? void 0 : newCardPreview.dataURI) !== (cardPreview === null || cardPreview === void 0 ? void 0 : cardPreview.dataURI)) {
351
- return;
352
- }
257
+ onImageLoadBase(newCardPreview);
353
258
  setPreviewDidRender(true);
354
259
  };
355
260
  const onCardClick = (event, analyticsEvent) => {
@@ -367,7 +272,7 @@ export const FileCard = ({
367
272
  return;
368
273
  }
369
274
  const isVideo = metadata && metadata.mediaType === 'video';
370
- if (useInlinePlayer && isVideo && !!cardPreview) {
275
+ if (useInlinePlayer && isVideo && !!cardPreview && finalStatus !== 'error') {
371
276
  setIsPlayingFile(true);
372
277
  setShouldAutoplay(true);
373
278
  } else if (shouldOpenMediaViewer) {
@@ -380,196 +285,6 @@ export const FileCard = ({
380
285
  }
381
286
  };
382
287
 
383
- //----------------------------------------------------------------//
384
- //---------------------- Helper Functions -----------------------//
385
- //----------------------------------------------------------------//
386
-
387
- const fetchRemotePreviewRef = useCurrentValueRef(identifier => {
388
- return fetchAndCacheRemotePreview(mediaClient, identifier.id, dimensions !== null && dimensions !== void 0 ? dimensions : {}, imageURLParams, getMediaBlobUrlAttrs(fileStateValue));
389
- });
390
- const resolvePreviewRef = useCurrentValueRef(async (identifier, fileState) => {
391
- const filePreview = isBannedLocalPreview ? undefined : fileState.status !== 'error' && 'mimeType' in fileState && isMimeTypeSupportedByBrowser(fileState.mimeType) ? fileState.preview : undefined;
392
- const isRemotePreviewReady = isImageRepresentationReady(fileState);
393
- try {
394
- const mode = imageURLParams.mode;
395
- const cachedPreview = cardPreviewCache.get(identifier.id, mode);
396
- const dimensionsAreBigger = isBigger(cachedPreview === null || cachedPreview === void 0 ? void 0 : cachedPreview.dimensions, dimensions);
397
- if (cachedPreview && !dimensionsAreBigger) {
398
- return cachedPreview;
399
- }
400
- let localPreview;
401
- try {
402
- if (filePreview) {
403
- let value;
404
- try {
405
- const resolvedFilePreview = await filePreview;
406
- value = resolvedFilePreview.value;
407
- } catch (e) {
408
- throw new LocalPreviewError('local-preview-rejected', e instanceof Error ? e : undefined);
409
- }
410
- if (typeof value === 'string') {
411
- localPreview = {
412
- dataURI: value,
413
- orientation: 1,
414
- source: 'local'
415
- };
416
- } else if (value instanceof Blob) {
417
- const {
418
- type
419
- } = value;
420
- const mediaType = getMediaTypeFromMimeType(type);
421
- switch (mediaType) {
422
- case 'image':
423
- try {
424
- const orientation = await getOrientation(value);
425
- const dataURI = URL.createObjectURL(value);
426
- localPreview = {
427
- dataURI,
428
- orientation,
429
- source: 'local'
430
- };
431
- } catch (e) {
432
- throw new LocalPreviewError('local-preview-image', e instanceof Error ? e : undefined);
433
- }
434
- break;
435
- case 'video':
436
- try {
437
- const dataURI = await takeSnapshot(value);
438
- localPreview = {
439
- dataURI,
440
- orientation: 1,
441
- source: 'local'
442
- };
443
- } catch (e) {
444
- throw new LocalPreviewError('local-preview-video', e instanceof Error ? e : undefined);
445
- }
446
- break;
447
- default:
448
- throw new LocalPreviewError('local-preview-unsupported');
449
- }
450
- } else {
451
- throw new LocalPreviewError('local-preview-unsupported');
452
- }
453
- const preview = {
454
- ...localPreview,
455
- dimensions
456
- };
457
- let source;
458
- switch (preview.source) {
459
- case 'local':
460
- source = 'cache-local';
461
- break;
462
- case 'remote':
463
- source = 'cache-remote';
464
- break;
465
- case 'ssr-server':
466
- source = 'cache-ssr-server';
467
- break;
468
- case 'ssr-client':
469
- source = 'cache-ssr-client';
470
- break;
471
- default:
472
- source = preview.source;
473
- }
474
- // We want to embed some meta context into dataURI for Copy/Paste to work.
475
- const mediaBlobUrlAttrs = getMediaBlobUrlAttrs(fileStateValue);
476
- const dataURI = mediaBlobUrlAttrs ? addFileAttrsToUrl(preview.dataURI, mediaBlobUrlAttrs) : preview.dataURI;
477
- // We store new cardPreview into cache
478
- cardPreviewCache.set(identifier.id, mode, {
479
- ...preview,
480
- source,
481
- dataURI
482
- });
483
- setCardPreview({
484
- ...preview,
485
- dataURI
486
- });
487
- return;
488
- }
489
- } catch (e) {
490
- /**
491
- * We report the error if:
492
- * - local preview is supported and fails
493
- * - local preview is unsupported and remote preview is NOT READY
494
- * i.e. the function was called for "no reason".
495
- * We DON'T report the error if:
496
- * - local preview is unsupported and remote preview IS READY
497
- * i.e. local preview is available and not supported,
498
- * but we are after the remote preview instead.
499
- */
500
- if (!isUnsupportedLocalPreviewError(e) || isUnsupportedLocalPreviewError(e) && !isRemotePreviewReady) {
501
- fireNonCriticalErrorEventRef.current && fireNonCriticalErrorEventRef.current(e);
502
- }
503
- /**
504
- * No matter the reason why the local preview failed, we break the process
505
- * if there is no remote preview available
506
- */
507
- if (!isRemotePreviewReady) {
508
- throw e;
509
- }
510
- }
511
- if (!isRemotePreviewReady) {
512
- /**
513
- * We throw this in case this function has been called
514
- * without checking isRemotePreviewReady first.
515
- * If remote preview is not ready, the call to getCardPreviewFromBackend
516
- * will generate a console error due to a 404 code
517
- */
518
- throw new MediaCardError('remote-preview-not-ready');
519
- }
520
- const remotePreview = await fetchAndCacheRemotePreview(mediaClient, identifier.id, dimensions !== null && dimensions !== void 0 ? dimensions : {}, imageURLParams, getMediaBlobUrlAttrs(fileStateValue), traceContext);
521
- setCardPreview(remotePreview);
522
- return;
523
- } catch (e) {
524
- const wrappedError = ensureMediaCardError('preview-fetch', e);
525
- // If remote preview fails, we set status 'error'
526
- // If local preview fails (i.e, no remote preview available),
527
- // we can stay in the same status until there is a remote preview available
528
- // If it's any other error we set status 'error'
529
- if (isLocalPreviewError(wrappedError)) {
530
- // This error should already been logged inside the getCardPreview. No need to log it here.
531
- setIsBannedLocalPreview(true);
532
- } else {
533
- if (!['complete', 'error', 'failed-processing'].includes(status)) {
534
- setStatus('error');
535
- setError(wrappedError);
536
- }
537
- }
538
- }
539
- });
540
-
541
- //----------------------------------------------------------------//
542
- //------------ resolveUpfrontPreview useEffect -------------------//
543
- //----------------------------------------------------------------//
544
- const prevCardPreview = usePrevious(cardPreview);
545
- const dimensionsRef = useCurrentValueRef(dimensions);
546
- useEffect(() => {
547
- const resolveUpfrontPreview = async identifier => {
548
- // We block any possible future call to this method regardless of the outcome (success or fail)
549
- // If it fails, the normal preview fetch should occur after the file state is fetched anyways
550
- wasResolvedUpfrontPreviewRef.current = true;
551
- try {
552
- const requestedDimensions = {
553
- ...dimensions
554
- };
555
- const newCardPreview = await fetchRemotePreviewRef.current(identifier);
556
- const areValidRequestedDimensions = !isBigger(requestedDimensions, dimensionsRef.current);
557
-
558
- // If there are new and bigger dimensions in the props, and the upfront preview is still resolving,
559
- // the fetched preview is no longer valid, and thus, we dismiss it
560
- if (areValidRequestedDimensions) {
561
- setCardPreview(newCardPreview);
562
- }
563
- } catch (e) {
564
- // NO need to log error. If this call fails, a refetch will happen after
565
- }
566
- };
567
- const hadSSRCardPreview = ssr === 'client' && !!prevCardPreview && isSSRClientPreview(prevCardPreview);
568
- if ((isCardVisible || hadSSRCardPreview) && !cardPreview && !wasResolvedUpfrontPreviewRef.current) {
569
- resolveUpfrontPreview(identifier);
570
- }
571
- }, [cardPreview, dimensions, dimensionsRef, fetchRemotePreviewRef, identifier, isCardVisible, prevCardPreview, ssr]);
572
-
573
288
  //----------------------------------------------------------------//
574
289
  //------------------------ handle fireCopiedEvent --------------//
575
290
  //----------------------------------------------------------------//
@@ -607,64 +322,19 @@ export const FileCard = ({
607
322
  //---------------- fetch and resolve card preview ----------------//
608
323
  //----------------------------------------------------------------//
609
324
 
610
- const prevDimensions = usePrevious(dimensions);
325
+ const prevStatus = usePrevious(finalStatus);
611
326
  const prevIsCardVisible = usePrevious(isCardVisible);
612
- const prevStatus = usePrevious(status);
613
327
  useEffect(() => {
614
- if (prevStatus !== undefined && status !== prevStatus) {
328
+ if (prevStatus !== undefined && finalStatus !== prevStatus) {
615
329
  fireOperationalEventRef.current();
616
330
  }
617
- }, [fireOperationalEventRef, prevStatus, status]);
331
+ }, [fireOperationalEventRef, prevStatus, finalStatus]);
618
332
  useEffect(() => {
619
- var _ssrDataRef$current3;
620
- /**
621
- * Variable turnedVisible should only be true when media card
622
- * was invisible in the previous state and is visible in the current one
623
- *
624
- * prevIsCardVisible | isCardVisible | turnedVisible
625
- * ----------------------------------------------------
626
- * false | false | false
627
- * false | true | true
628
- * true | true | false
629
- * true | false | false (unreachable case)
630
- * ----------------------------------------------------
631
- */
632
-
633
333
  const turnedVisible = !prevIsCardVisible && isCardVisible;
634
334
  if (turnedVisible) {
635
335
  fireCommencedEventRef.current();
636
336
  }
637
- if (cardPreview && turnedVisible && isSSRDataPreview(cardPreview) && isBigger((_ssrDataRef$current3 = ssrDataRef.current) === null || _ssrDataRef$current3 === void 0 ? void 0 : _ssrDataRef$current3.dimensions, dimensions)) {
638
- // If dimensions from Server have changed and are bigger,
639
- // we need to refetch
640
- // refetchSRRPreview
641
- fetchRemotePreviewRef.current(identifier).then(setCardPreview).catch(e => {
642
- const wrappedError = ensureMediaCardError('remote-preview-fetch-ssr', e, true);
643
- fireNonCriticalErrorEventRef.current(wrappedError);
644
- });
645
- }
646
- if (fileStateValue && shouldResolvePreview({
647
- status,
648
- fileState: fileStateValue,
649
- prevDimensions,
650
- dimensions,
651
- hasCardPreview: !!cardPreview,
652
- isBannedLocalPreview,
653
- wasResolvedUpfrontPreview: wasResolvedUpfrontPreviewRef.current
654
- })) {
655
- resolvePreviewRef.current(identifier, fileStateValue);
656
- }
657
- if (turnedVisible && ssr && !!cardPreview && isSSRClientPreview(cardPreview)) {
658
- // Since the SSR preview brings the token in the query params,
659
- // We need to fetch the remote preview to be able to cache it,
660
- fetchRemotePreviewRef.current(identifier).catch(() => {
661
- // No need to log this error.
662
- // If preview fails, it will be refetched later
663
- //TODO: test this catch
664
- // https://product-fabric.atlassian.net/browse/MEX-1071
665
- });
666
- }
667
- }, [cardPreview, dimensions, fetchRemotePreviewRef, fileStateValue, fireCommencedEventRef, fireNonCriticalErrorEventRef, identifier, isBannedLocalPreview, isCardVisible, prevDimensions, prevIsCardVisible, resolvePreviewRef, ssr, status]);
337
+ }, [fireCommencedEventRef, isCardVisible, prevIsCardVisible]);
668
338
 
669
339
  //----------------------------------------------------------------//
670
340
  //----------------- set complete status --------------------------//
@@ -673,12 +343,12 @@ export const FileCard = ({
673
343
  useEffect(() => {
674
344
  if (previewDidRender &&
675
345
  // We should't complete if status is uploading
676
- ['loading-preview', 'processing'].includes(status)) {
346
+ ['loading-preview', 'processing'].includes(finalStatus)) {
677
347
  setStatus('complete');
678
348
  // TODO MEX-788: add test for "do not remove the card preview when unsubscribing".
679
349
  setIsBannedLocalPreview(false);
680
350
  }
681
- }, [previewDidRender, status]);
351
+ }, [previewDidRender, finalStatus]);
682
352
 
683
353
  //----------------------------------------------------------------//
684
354
  //----------------- set isPlayingFile state ----------------------//
@@ -696,22 +366,22 @@ export const FileCard = ({
696
366
  * in order to avoid race conditions of the ViewportDector being unmounted before
697
367
  * it is able to set isCardVisible to true.
698
368
  */
699
- isCardVisible && isVideo && !isPlayingFile && disableOverlay && useInlinePlayer && isVideoPlayable) {
369
+ isCardVisible && isVideo && !isPlayingFile && disableOverlay && useInlinePlayer && isVideoPlayable && finalStatus !== 'error') {
700
370
  setIsPlayingFile(true);
701
371
  }
702
- }, [isCardVisible, disableOverlay, fileAttributes.fileMediatype, fileStateValue, identifier, isBannedLocalPreview, isPlayingFile, useInlinePlayer]);
372
+ }, [isCardVisible, disableOverlay, fileAttributes.fileMediatype, fileStateValue, identifier, isBannedLocalPreview, isPlayingFile, finalStatus, useInlinePlayer]);
703
373
 
704
374
  //----------------------------------------------------------------//
705
375
  //----------------- fireScreenEvent ------------------------------//
706
376
  //----------------------------------------------------------------//
707
377
 
708
378
  useEffect(() => {
709
- if (prevStatus !== undefined && status !== prevStatus) {
710
- if (status === 'complete' || fileAttributes.fileMediatype === 'video' && !!cardPreview && status === 'processing') {
379
+ if (prevStatus !== undefined && finalStatus !== prevStatus) {
380
+ if (finalStatus === 'complete' || fileAttributes.fileMediatype === 'video' && !!cardPreview && finalStatus === 'processing') {
711
381
  fireScreenEventRef.current();
712
382
  }
713
383
  }
714
- }, [status, prevStatus, fileAttributes, cardPreview, fireScreenEventRef]);
384
+ }, [finalStatus, prevStatus, fileAttributes, cardPreview, fireScreenEventRef]);
715
385
 
716
386
  //----------------------------------------------------------------//
717
387
  //----------------- abort UFO experience -------------------------//
@@ -731,7 +401,7 @@ export const FileCard = ({
731
401
  const updateFileStateRef = useCurrentValueRef(() => {
732
402
  if (fileState) {
733
403
  // do not update the card status if the status is final
734
- if (['complete', 'error', 'failed-processing'].includes(status)) {
404
+ if (['complete', 'error', 'failed-processing'].includes(finalStatus)) {
735
405
  return;
736
406
  }
737
407
  if (fileState.status !== 'error') {
@@ -739,7 +409,9 @@ export const FileCard = ({
739
409
  const isPreviewable = !!mediaType && ['audio', 'video', 'image', 'doc'].indexOf(mediaType) > -1;
740
410
  const isPreviewableFileState = !!fileState.preview;
741
411
  const isSupportedLocalPreview = mediaType === 'image' || mediaType === 'video';
742
- const hasLocalPreview = !isBannedLocalPreview && isPreviewableFileState && isSupportedLocalPreview && !!fileState.mimeType && isMimeTypeSupportedByBrowser(fileState.mimeType);
412
+ const hasLocalPreview = !isBannedLocalPreview && isPreviewableFileState &&
413
+ // CXP-2723 TODO: isPreviewableFileState is most likely redundant
414
+ isSupportedLocalPreview && !!fileState.mimeType && isMimeTypeSupportedByBrowser(fileState.mimeType);
743
415
  const hasRemotePreview = isImageRepresentationReady(fileState);
744
416
  const hasPreview = hasLocalPreview || hasRemotePreview;
745
417
  let newStatus;
@@ -764,7 +436,7 @@ export const FileCard = ({
764
436
  uploadProgressRef.current = newProgress;
765
437
  } else {
766
438
  const e = new MediaFileStateError(fileState.id, fileState.reason, fileState.message, fileState.details);
767
- const errorReason = status === 'uploading' ? 'upload' : 'metadata-fetch';
439
+ const errorReason = finalStatus === 'uploading' ? 'upload' : 'metadata-fetch';
768
440
  setError(new MediaCardError(errorReason, e));
769
441
  setStatus('error');
770
442
  }
@@ -784,15 +456,18 @@ export const FileCard = ({
784
456
  } = identifier;
785
457
  const isLazyWithOverride = izLazyOverride === undefined ? isLazy : izLazyOverride;
786
458
 
787
- // Card can be artificially turned visible before entering the viewport
788
- // For example, when we have the image in cache
789
- const nativeLazyLoad = isLazyWithOverride && !isCardVisible;
459
+ // We should natively lazy load an SSR preview when card is not visible,
460
+ // otherwise we'll fire the metadata fetch from outside the viewport
461
+ // Side note: We should not lazy load if the cardPreview is available from local cache,
462
+ // in order to avoid flickers during re-mount of the component
463
+ // CXP-2723 TODO: Create test cases for the above scenarios
464
+ const nativeLazyLoad = isLazyWithOverride && !isCardVisible && cardPreview && isSSRPreview(cardPreview);
790
465
  // Force Media Image to always display img for SSR
791
466
  const forceSyncDisplay = !!ssr;
792
- const mediaCardCursor = getMediaCardCursor(!!useInlinePlayer, !!shouldOpenMediaViewer, status === 'error' || status === 'failed-processing', !!cardPreview, metadata.mediaType);
467
+ const mediaCardCursor = getMediaCardCursor(!!useInlinePlayer, !!shouldOpenMediaViewer, finalStatus === 'error' || finalStatus === 'failed-processing', !!cardPreview, metadata.mediaType);
793
468
  const card = /*#__PURE__*/React.createElement(CardViewV2, {
794
- status: cardStatusOverride || status,
795
- error: error,
469
+ status: cardStatusOverride || finalStatus,
470
+ error: finalError,
796
471
  mediaItemType: mediaItemType,
797
472
  metadata: metadata,
798
473
  cardPreview: cardPreview,
@@ -856,7 +531,9 @@ export const FileCard = ({
856
531
  identifier: identifier,
857
532
  autoplay: !!shouldAutoplay,
858
533
  onFullscreenChange: onFullscreenChange,
859
- onError: () => {
534
+ onError: e => {
535
+ setError(new MediaCardError('error-file-state', e));
536
+ setStatus('error');
860
537
  setIsPlayingFile(false);
861
538
  },
862
539
  onClick: onCardClick,
@@ -874,13 +551,5 @@ export const FileCard = ({
874
551
  },
875
552
  contextId: contextId,
876
553
  featureFlags: featureFlags
877
- }), document.body) : null, ssr === 'server' && /*#__PURE__*/React.createElement(StoreSSRDataScript, {
878
- identifier: identifier,
879
- dataURI: cardPreview === null || cardPreview === void 0 ? void 0 : cardPreview.dataURI,
880
- dimensions: getRequestedDimensions({
881
- dimensions,
882
- element: cardElement
883
- }),
884
- error: ((_ssrReliabilityRef$cu = ssrReliabilityRef.current.server) === null || _ssrReliabilityRef$cu === void 0 ? void 0 : _ssrReliabilityRef$cu.status) === 'fail' ? ssrReliabilityRef.current.server : undefined
885
- }));
554
+ }), document.body) : null, ssr === 'server' && /*#__PURE__*/React.createElement(StoreSSRDataScript, null));
886
555
  };