@atlaskit/media-card 77.2.3 → 77.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/dist/cjs/card/card.js +1 -1
- package/dist/cjs/card/getCardPreview/cache.js +1 -1
- package/dist/cjs/card/media-card-analytics-error-boundary.js +1 -1
- package/dist/cjs/card/ui/imageRenderer/imageRenderer.js +6 -3
- package/dist/cjs/card/v2/cardV2.js +18 -1037
- package/dist/cjs/card/v2/externalImageCard.js +299 -0
- package/dist/cjs/card/v2/fileCard.js +1001 -0
- package/dist/cjs/errors.js +33 -55
- package/dist/cjs/inline/loader.js +1 -1
- package/dist/cjs/utils/ufoExperiences.js +1 -1
- package/dist/cjs/utils/useCurrentValueRef.js +12 -0
- package/dist/cjs/utils/usePrevious.js +14 -0
- package/dist/es2019/card/card.js +1 -1
- package/dist/es2019/card/getCardPreview/cache.js +2 -0
- package/dist/es2019/card/media-card-analytics-error-boundary.js +1 -1
- package/dist/es2019/card/ui/imageRenderer/imageRenderer.js +6 -3
- package/dist/es2019/card/v2/cardV2.js +17 -1021
- package/dist/es2019/card/v2/externalImageCard.js +261 -0
- package/dist/es2019/card/v2/fileCard.js +881 -0
- package/dist/es2019/errors.js +1 -17
- package/dist/es2019/inline/loader.js +1 -1
- package/dist/es2019/utils/ufoExperiences.js +1 -1
- package/dist/es2019/utils/useCurrentValueRef.js +6 -0
- package/dist/es2019/utils/usePrevious.js +8 -0
- package/dist/esm/card/card.js +1 -1
- package/dist/esm/card/getCardPreview/cache.js +2 -0
- package/dist/esm/card/media-card-analytics-error-boundary.js +1 -1
- package/dist/esm/card/ui/imageRenderer/imageRenderer.js +6 -3
- package/dist/esm/card/v2/cardV2.js +19 -1036
- package/dist/esm/card/v2/externalImageCard.js +289 -0
- package/dist/esm/card/v2/fileCard.js +991 -0
- package/dist/esm/errors.js +31 -55
- package/dist/esm/inline/loader.js +1 -1
- package/dist/esm/utils/ufoExperiences.js +1 -1
- package/dist/esm/utils/useCurrentValueRef.js +6 -0
- package/dist/esm/utils/usePrevious.js +8 -0
- package/dist/types/card/v2/cardV2.d.ts +4 -60
- package/dist/types/card/v2/externalImageCard.d.ts +14 -0
- package/dist/types/card/v2/fileCard.d.ts +19 -0
- package/dist/types/errors.d.ts +1 -7
- package/dist/types/utils/useCurrentValueRef.d.ts +2 -0
- package/dist/types/utils/usePrevious.d.ts +1 -0
- package/dist/types-ts4.5/card/v2/cardV2.d.ts +4 -60
- package/dist/types-ts4.5/card/v2/externalImageCard.d.ts +14 -0
- package/dist/types-ts4.5/card/v2/fileCard.d.ts +19 -0
- package/dist/types-ts4.5/errors.d.ts +1 -7
- package/dist/types-ts4.5/utils/useCurrentValueRef.d.ts +2 -0
- package/dist/types-ts4.5/utils/usePrevious.d.ts +1 -0
- package/package.json +5 -5
|
@@ -0,0 +1,881 @@
|
|
|
1
|
+
import DownloadIcon from '@atlaskit/icon/glyph/download';
|
|
2
|
+
import { RECENTS_COLLECTION, addFileAttrsToUrl, globalMediaEventEmitter, imageResizeModeToFileImageMode, isFileIdentifier, isImageRepresentationReady } from '@atlaskit/media-client';
|
|
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';
|
|
6
|
+
import { MediaViewer } from '@atlaskit/media-viewer';
|
|
7
|
+
import React, { Suspense, useEffect, useMemo, useRef, useState } from 'react';
|
|
8
|
+
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';
|
|
12
|
+
import getDocument from '../../utils/document';
|
|
13
|
+
import { generateUniqueId } from '../../utils/generateUniqueId';
|
|
14
|
+
import { getRequestedDimensions } from '../../utils/getDataURIDimension';
|
|
15
|
+
import { getMediaCardCursor } from '../../utils/getMediaCardCursor';
|
|
16
|
+
import { getSSRData, StoreSSRDataScript } from '../../utils/globalScope';
|
|
17
|
+
import { getFileDetails } from '../../utils/metadata';
|
|
18
|
+
import { abortUfoExperience, completeUfoExperience, startUfoExperience } from '../../utils/ufoExperiences';
|
|
19
|
+
import { useCurrentValueRef } from '../../utils/useCurrentValueRef';
|
|
20
|
+
import { usePrevious } from '../../utils/usePrevious';
|
|
21
|
+
import { videoIsPlayable } from '../../utils/videoIsPlayable';
|
|
22
|
+
import { takeSnapshot } from '../../utils/videoSnapshot';
|
|
23
|
+
import { ViewportDetector } from '../../utils/viewportDetector';
|
|
24
|
+
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';
|
|
27
|
+
import { CardViewV2 } from './cardViewV2';
|
|
28
|
+
import { InlinePlayerLazyV2 } from './inlinePlayerLazyV2';
|
|
29
|
+
export const FileCard = ({
|
|
30
|
+
appearance = 'auto',
|
|
31
|
+
resizeMode = 'crop',
|
|
32
|
+
isLazy = true,
|
|
33
|
+
disableOverlay = false,
|
|
34
|
+
// Media Feature Flag defaults are defined in @atlaskit/media-common
|
|
35
|
+
featureFlags = {},
|
|
36
|
+
identifier,
|
|
37
|
+
ssr,
|
|
38
|
+
mediaClient,
|
|
39
|
+
dimensions,
|
|
40
|
+
originalDimensions,
|
|
41
|
+
contextId,
|
|
42
|
+
alt,
|
|
43
|
+
actions,
|
|
44
|
+
shouldEnableDownloadButton,
|
|
45
|
+
useInlinePlayer,
|
|
46
|
+
shouldOpenMediaViewer,
|
|
47
|
+
onFullscreenChange,
|
|
48
|
+
selectable,
|
|
49
|
+
selected,
|
|
50
|
+
testId,
|
|
51
|
+
titleBoxBgColor,
|
|
52
|
+
titleBoxIcon,
|
|
53
|
+
shouldHideTooltip,
|
|
54
|
+
mediaViewerItems,
|
|
55
|
+
onClick,
|
|
56
|
+
onMouseEnter,
|
|
57
|
+
createAnalyticsEvent
|
|
58
|
+
}) => {
|
|
59
|
+
var _ssrReliabilityRef$cu;
|
|
60
|
+
//----------------------------------------------------------------//
|
|
61
|
+
//---------------- State Initializer Functions -------------------//
|
|
62
|
+
//----------------------------------------------------------------//
|
|
63
|
+
|
|
64
|
+
const {
|
|
65
|
+
fileState
|
|
66
|
+
} = useFileState(identifier.id, {
|
|
67
|
+
collectionName: identifier.collectionName,
|
|
68
|
+
occurrenceKey: identifier.occurrenceKey
|
|
69
|
+
});
|
|
70
|
+
const prevFileState = usePrevious(fileState);
|
|
71
|
+
const fileStateValue = useMemo(() => {
|
|
72
|
+
if (fileState && (fileState === null || fileState === void 0 ? void 0 : fileState.status) !== 'error') {
|
|
73
|
+
return fileState;
|
|
74
|
+
}
|
|
75
|
+
return prevFileState;
|
|
76
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
77
|
+
}, [fileState]);
|
|
78
|
+
const ssrDataRef = useRef();
|
|
79
|
+
const ssrReliabilityRef = useRef({
|
|
80
|
+
server: {
|
|
81
|
+
status: 'unknown'
|
|
82
|
+
},
|
|
83
|
+
client: {
|
|
84
|
+
status: 'unknown'
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
const [cardElement, setCardElement] = useState(null);
|
|
88
|
+
const imageURLParams = useMemo(() => ({
|
|
89
|
+
collection: identifier.collectionName,
|
|
90
|
+
mode: resizeMode === 'stretchy-fit' ? 'full-fit' : resizeMode,
|
|
91
|
+
...getRequestedDimensions({
|
|
92
|
+
dimensions,
|
|
93
|
+
element: cardElement
|
|
94
|
+
}),
|
|
95
|
+
allowAnimated: true
|
|
96
|
+
}), [cardElement, dimensions, identifier.collectionName, resizeMode]);
|
|
97
|
+
const mediaBlobUrlAttrs = useMemo(() => {
|
|
98
|
+
const {
|
|
99
|
+
id,
|
|
100
|
+
collectionName: collection
|
|
101
|
+
} = identifier;
|
|
102
|
+
const {
|
|
103
|
+
mimeType,
|
|
104
|
+
name,
|
|
105
|
+
size
|
|
106
|
+
} = getFileDetails(identifier, fileStateValue);
|
|
107
|
+
return contextId ? {
|
|
108
|
+
id,
|
|
109
|
+
collection,
|
|
110
|
+
contextId,
|
|
111
|
+
mimeType,
|
|
112
|
+
name,
|
|
113
|
+
size,
|
|
114
|
+
...(originalDimensions || getRequestedDimensions({
|
|
115
|
+
dimensions,
|
|
116
|
+
element: cardElement
|
|
117
|
+
})),
|
|
118
|
+
alt
|
|
119
|
+
} : undefined;
|
|
120
|
+
}, [alt, cardElement, contextId, dimensions, fileStateValue, identifier, originalDimensions]);
|
|
121
|
+
const getSSRPreview = (ssr, identifier, mediaClient) => {
|
|
122
|
+
var _ssrDataRef$current, _ssrDataRef$current2;
|
|
123
|
+
ssrDataRef.current = getSSRData(identifier);
|
|
124
|
+
if ((_ssrDataRef$current = ssrDataRef.current) !== null && _ssrDataRef$current !== void 0 && _ssrDataRef$current.error) {
|
|
125
|
+
ssrReliabilityRef.current.server = {
|
|
126
|
+
status: 'fail',
|
|
127
|
+
...ssrDataRef.current.error
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
if (!((_ssrDataRef$current2 = ssrDataRef.current) !== null && _ssrDataRef$current2 !== void 0 && _ssrDataRef$current2.dataURI)) {
|
|
131
|
+
try {
|
|
132
|
+
return getSSRCardPreview(ssr, mediaClient, identifier.id, imageURLParams, mediaBlobUrlAttrs);
|
|
133
|
+
} catch (e) {
|
|
134
|
+
ssrReliabilityRef.current[ssr] = {
|
|
135
|
+
status: 'fail',
|
|
136
|
+
...extractErrorInfo(e)
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
} else {
|
|
140
|
+
return {
|
|
141
|
+
dataURI: ssrDataRef.current.dataURI,
|
|
142
|
+
source: 'ssr-data'
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
const cardPreviewInitializer = () => {
|
|
147
|
+
let cardPreview;
|
|
148
|
+
const {
|
|
149
|
+
id
|
|
150
|
+
} = identifier;
|
|
151
|
+
const fileImageMode = imageResizeModeToFileImageMode(resizeMode);
|
|
152
|
+
cardPreview = getCardPreviewFromCache(id, fileImageMode);
|
|
153
|
+
if (!cardPreview && ssr) {
|
|
154
|
+
cardPreview = getSSRPreview(ssr, identifier, mediaClient);
|
|
155
|
+
}
|
|
156
|
+
return cardPreview;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
//----------------------------------------------------------------//
|
|
160
|
+
//------------ State, Refs & Initial Values ----------------------//
|
|
161
|
+
//----------------------------------------------------------------//
|
|
162
|
+
|
|
163
|
+
const internalOccurrenceKey = useMemo(() => generateUniqueId(), []);
|
|
164
|
+
const timeElapsedTillCommenced = useMemo(() => performance.now(), []);
|
|
165
|
+
const fileStateFlagsRef = useRef({
|
|
166
|
+
wasStatusUploading: false,
|
|
167
|
+
wasStatusProcessing: false
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// Generate unique traceId for file
|
|
171
|
+
const traceContext = useMemo(() => ({
|
|
172
|
+
traceId: getRandomHex(8)
|
|
173
|
+
}), []);
|
|
174
|
+
const [status, setStatus] = useState('loading');
|
|
175
|
+
useEffect(() => {
|
|
176
|
+
setStatus('loading');
|
|
177
|
+
}, [identifier]);
|
|
178
|
+
const [cardPreview, setCardPreview] = useState(cardPreviewInitializer);
|
|
179
|
+
|
|
180
|
+
// If cardPreview is available from local cache or external, `isCardVisible`
|
|
181
|
+
// should be true to avoid flickers during re-mount of the component
|
|
182
|
+
// should not be visible for SSR preview, otherwise we'll fire the metadata fetch from
|
|
183
|
+
// outside the viewport
|
|
184
|
+
const [isCardVisible, setIsCardVisible] = useState(() => !isLazy || !!cardPreview && !isSSRPreview(cardPreview));
|
|
185
|
+
const [isPlayingFile, setIsPlayingFile] = useState(false);
|
|
186
|
+
const [shouldAutoplay, setShouldAutoplay] = useState(false);
|
|
187
|
+
const [isBannedLocalPreview, setIsBannedLocalPreview] = useState(false);
|
|
188
|
+
const [previewDidRender, setPreviewDidRender] = useState(false);
|
|
189
|
+
const [error, setError] = useState();
|
|
190
|
+
const wasResolvedUpfrontPreviewRef = useRef(false);
|
|
191
|
+
const [mediaViewerSelectedItem, setMediaViewerSelectedItem] = useState(null);
|
|
192
|
+
const uploadProgressRef = useRef();
|
|
193
|
+
const metadata = useMemo(() => {
|
|
194
|
+
const getProcessingStatusFromFileState = status => {
|
|
195
|
+
switch (status) {
|
|
196
|
+
case 'processed':
|
|
197
|
+
return 'succeeded';
|
|
198
|
+
case 'processing':
|
|
199
|
+
return 'running';
|
|
200
|
+
case 'failed-processing':
|
|
201
|
+
return 'failed';
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
if (fileStateValue && (fileStateValue === null || fileStateValue === void 0 ? void 0 : fileStateValue.status) !== 'error') {
|
|
205
|
+
return {
|
|
206
|
+
id: fileStateValue.id,
|
|
207
|
+
name: fileStateValue.name,
|
|
208
|
+
size: fileStateValue.size,
|
|
209
|
+
mimeType: fileStateValue.mimeType,
|
|
210
|
+
createdAt: fileStateValue.createdAt,
|
|
211
|
+
mediaType: fileStateValue.mediaType,
|
|
212
|
+
processingStatus: getProcessingStatusFromFileState(fileStateValue.status)
|
|
213
|
+
};
|
|
214
|
+
} else {
|
|
215
|
+
return {
|
|
216
|
+
id: identifier.id
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
}, [fileStateValue, identifier.id]);
|
|
220
|
+
const fileAttributes = useMemo(() => {
|
|
221
|
+
return {
|
|
222
|
+
fileMediatype: metadata.mediaType,
|
|
223
|
+
fileMimetype: metadata.mimeType,
|
|
224
|
+
fileId: metadata.id,
|
|
225
|
+
fileSize: metadata.size,
|
|
226
|
+
fileStatus: fileStateValue === null || fileStateValue === void 0 ? void 0 : fileStateValue.status
|
|
227
|
+
};
|
|
228
|
+
}, [fileStateValue === null || fileStateValue === void 0 ? void 0 : fileStateValue.status, metadata.id, metadata.mediaType, metadata.mimeType, metadata.size]);
|
|
229
|
+
const computedActions = useMemo(() => {
|
|
230
|
+
if (status === 'failed-processing' || shouldEnableDownloadButton) {
|
|
231
|
+
const downloadAction = {
|
|
232
|
+
label: 'Download',
|
|
233
|
+
icon: /*#__PURE__*/React.createElement(DownloadIcon, {
|
|
234
|
+
label: "Download"
|
|
235
|
+
}),
|
|
236
|
+
handler: () => mediaClient.file.downloadBinary(identifier.id, metadata.name, identifier.collectionName)
|
|
237
|
+
};
|
|
238
|
+
return [downloadAction, ...(actions !== null && actions !== void 0 ? actions : [])];
|
|
239
|
+
} else {
|
|
240
|
+
return actions;
|
|
241
|
+
}
|
|
242
|
+
}, [actions, identifier.collectionName, identifier.id, mediaClient.file, metadata.name, shouldEnableDownloadButton, status]);
|
|
243
|
+
|
|
244
|
+
//----------------------------------------------------------------//
|
|
245
|
+
//---------------------- Analytics ------------------------------//
|
|
246
|
+
//----------------------------------------------------------------//
|
|
247
|
+
|
|
248
|
+
const fireOperationalEventRef = useCurrentValueRef(() => {
|
|
249
|
+
const timeElapsedTillEvent = performance.now();
|
|
250
|
+
const durationSinceCommenced = timeElapsedTillEvent - timeElapsedTillCommenced;
|
|
251
|
+
const performanceAttributes = {
|
|
252
|
+
overall: {
|
|
253
|
+
durationSincePageStart: timeElapsedTillEvent,
|
|
254
|
+
durationSinceCommenced
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
createAnalyticsEvent && fireOperationalEvent(createAnalyticsEvent, status, fileAttributes, performanceAttributes, ssrReliabilityRef.current, error, traceContext, fileStateValue === null || fileStateValue === void 0 ? void 0 : fileStateValue.metadataTraceContext);
|
|
258
|
+
completeUfoExperience(internalOccurrenceKey, status, fileAttributes, fileStateFlagsRef.current, ssrReliabilityRef.current, error);
|
|
259
|
+
});
|
|
260
|
+
const fireNonCriticalErrorEventRef = useCurrentValueRef(error => {
|
|
261
|
+
createAnalyticsEvent && fireNonCriticalErrorEvent(createAnalyticsEvent, status, fileAttributes, ssrReliabilityRef.current, error, traceContext, fileStateValue === null || fileStateValue === void 0 ? void 0 : fileStateValue.metadataTraceContext);
|
|
262
|
+
});
|
|
263
|
+
const fireScreenEventRef = useCurrentValueRef(() => {
|
|
264
|
+
createAnalyticsEvent && fireScreenEvent(createAnalyticsEvent, fileAttributes);
|
|
265
|
+
});
|
|
266
|
+
const fireCommencedEventRef = useCurrentValueRef(() => {
|
|
267
|
+
createAnalyticsEvent && fireCommencedEvent(createAnalyticsEvent, fileAttributes, {
|
|
268
|
+
overall: {
|
|
269
|
+
durationSincePageStart: timeElapsedTillCommenced
|
|
270
|
+
}
|
|
271
|
+
}, traceContext);
|
|
272
|
+
startUfoExperience(internalOccurrenceKey);
|
|
273
|
+
});
|
|
274
|
+
const fireAbortedEventRef = useCurrentValueRef(() => {
|
|
275
|
+
// UFO won't abort if it's already in a final state (succeeded, failed, aborted, etc)
|
|
276
|
+
abortUfoExperience(internalOccurrenceKey, {
|
|
277
|
+
fileAttributes,
|
|
278
|
+
fileStateFlags: fileStateFlagsRef === null || fileStateFlagsRef === void 0 ? void 0 : fileStateFlagsRef.current,
|
|
279
|
+
ssrReliability: ssrReliabilityRef === null || ssrReliabilityRef === void 0 ? void 0 : ssrReliabilityRef.current
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
//----------------------------------------------------------------//
|
|
284
|
+
//---------------------- Callbacks & Handlers -------------------//
|
|
285
|
+
//----------------------------------------------------------------//
|
|
286
|
+
|
|
287
|
+
const onImageError = newCardPreview => {
|
|
288
|
+
if (newCardPreview) {
|
|
289
|
+
const failedSSRObject = {
|
|
290
|
+
status: 'fail',
|
|
291
|
+
...extractErrorInfo(new ImageLoadError(newCardPreview.source))
|
|
292
|
+
};
|
|
293
|
+
if (isSSRClientPreview(newCardPreview)) {
|
|
294
|
+
ssrReliabilityRef.current.client = failedSSRObject;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/*
|
|
298
|
+
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.
|
|
299
|
+
*/
|
|
300
|
+
|
|
301
|
+
if (isSSRDataPreview(newCardPreview)) {
|
|
302
|
+
ssrReliabilityRef.current.server = failedSSRObject;
|
|
303
|
+
ssrReliabilityRef.current.client = failedSSRObject;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// If the dataURI has been replaced, we can dismiss this error
|
|
308
|
+
if ((newCardPreview === null || newCardPreview === void 0 ? void 0 : newCardPreview.dataURI) !== (cardPreview === null || cardPreview === void 0 ? void 0 : cardPreview.dataURI)) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
const error = new ImageLoadError(newCardPreview === null || newCardPreview === void 0 ? void 0 : newCardPreview.source);
|
|
312
|
+
const isLocal = newCardPreview && isLocalPreview(newCardPreview);
|
|
313
|
+
const isSSR = newCardPreview && (isSSRClientPreview(newCardPreview) || isSSRDataPreview(newCardPreview));
|
|
314
|
+
if (isLocal || isSSR) {
|
|
315
|
+
if (isLocal) {
|
|
316
|
+
setIsBannedLocalPreview(true);
|
|
317
|
+
fireNonCriticalErrorEventRef.current && fireNonCriticalErrorEventRef.current(error);
|
|
318
|
+
}
|
|
319
|
+
const fileImageMode = imageResizeModeToFileImageMode(resizeMode);
|
|
320
|
+
isFileIdentifier(identifier) && removeCardPreviewFromCache(identifier.id, fileImageMode);
|
|
321
|
+
setCardPreview(undefined);
|
|
322
|
+
} else {
|
|
323
|
+
if (!['complete', 'error', 'failed-processing'].includes(status)) {
|
|
324
|
+
setStatus('error');
|
|
325
|
+
setError(error);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
const onImageLoad = newCardPreview => {
|
|
330
|
+
if (newCardPreview) {
|
|
331
|
+
if (isSSRClientPreview(newCardPreview) && ssrReliabilityRef.current.client.status === 'unknown') {
|
|
332
|
+
ssrReliabilityRef.current.client = {
|
|
333
|
+
status: 'success'
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/*
|
|
338
|
+
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.
|
|
339
|
+
*/
|
|
340
|
+
|
|
341
|
+
if (isSSRDataPreview(newCardPreview) && ssrReliabilityRef.current.server.status === 'unknown') {
|
|
342
|
+
ssrReliabilityRef.current.server = {
|
|
343
|
+
status: 'success'
|
|
344
|
+
};
|
|
345
|
+
ssrReliabilityRef.current.client = {
|
|
346
|
+
status: 'success'
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// If the dataURI has been replaced, we can dismiss this callback
|
|
352
|
+
if ((newCardPreview === null || newCardPreview === void 0 ? void 0 : newCardPreview.dataURI) !== (cardPreview === null || cardPreview === void 0 ? void 0 : cardPreview.dataURI)) {
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
setPreviewDidRender(true);
|
|
356
|
+
};
|
|
357
|
+
const onCardClick = (event, analyticsEvent) => {
|
|
358
|
+
if (onClick) {
|
|
359
|
+
const cardEvent = {
|
|
360
|
+
event,
|
|
361
|
+
mediaItemDetails: metadata
|
|
362
|
+
};
|
|
363
|
+
onClick(cardEvent, analyticsEvent);
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
const onCardViewClick = (event, analyticsEvent) => {
|
|
367
|
+
onCardClick(event, analyticsEvent);
|
|
368
|
+
if (!metadata) {
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
const isVideo = metadata && metadata.mediaType === 'video';
|
|
372
|
+
if (useInlinePlayer && isVideo && !!cardPreview) {
|
|
373
|
+
setIsPlayingFile(true);
|
|
374
|
+
setShouldAutoplay(true);
|
|
375
|
+
} else if (shouldOpenMediaViewer) {
|
|
376
|
+
setMediaViewerSelectedItem({
|
|
377
|
+
id: identifier.id,
|
|
378
|
+
mediaItemType: 'file',
|
|
379
|
+
collectionName: identifier.collectionName,
|
|
380
|
+
occurrenceKey: identifier.occurrenceKey
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
//----------------------------------------------------------------//
|
|
386
|
+
//---------------------- Helper Functions -----------------------//
|
|
387
|
+
//----------------------------------------------------------------//
|
|
388
|
+
|
|
389
|
+
const fetchRemotePreviewRef = useCurrentValueRef(identifier => {
|
|
390
|
+
return fetchAndCacheRemotePreview(mediaClient, identifier.id, dimensions !== null && dimensions !== void 0 ? dimensions : {}, imageURLParams, mediaBlobUrlAttrs);
|
|
391
|
+
});
|
|
392
|
+
const resolvePreviewRef = useCurrentValueRef(async (identifier, fileState) => {
|
|
393
|
+
const filePreview = isBannedLocalPreview ? undefined : fileState.status !== 'error' && 'mimeType' in fileState && isMimeTypeSupportedByBrowser(fileState.mimeType) ? fileState.preview : undefined;
|
|
394
|
+
const isRemotePreviewReady = isImageRepresentationReady(fileState);
|
|
395
|
+
try {
|
|
396
|
+
const mode = imageURLParams.mode;
|
|
397
|
+
const cachedPreview = cardPreviewCache.get(identifier.id, mode);
|
|
398
|
+
const dimensionsAreBigger = isBigger(cachedPreview === null || cachedPreview === void 0 ? void 0 : cachedPreview.dimensions, dimensions);
|
|
399
|
+
if (cachedPreview && !dimensionsAreBigger) {
|
|
400
|
+
return cachedPreview;
|
|
401
|
+
}
|
|
402
|
+
let localPreview;
|
|
403
|
+
try {
|
|
404
|
+
if (filePreview) {
|
|
405
|
+
let value;
|
|
406
|
+
try {
|
|
407
|
+
const resolvedFilePreview = await filePreview;
|
|
408
|
+
value = resolvedFilePreview.value;
|
|
409
|
+
} catch (e) {
|
|
410
|
+
throw new LocalPreviewError('local-preview-rejected', e instanceof Error ? e : undefined);
|
|
411
|
+
}
|
|
412
|
+
if (typeof value === 'string') {
|
|
413
|
+
localPreview = {
|
|
414
|
+
dataURI: value,
|
|
415
|
+
orientation: 1,
|
|
416
|
+
source: 'local'
|
|
417
|
+
};
|
|
418
|
+
} else if (value instanceof Blob) {
|
|
419
|
+
const {
|
|
420
|
+
type
|
|
421
|
+
} = value;
|
|
422
|
+
const mediaType = getMediaTypeFromMimeType(type);
|
|
423
|
+
switch (mediaType) {
|
|
424
|
+
case 'image':
|
|
425
|
+
try {
|
|
426
|
+
const orientation = await getOrientation(value);
|
|
427
|
+
const dataURI = URL.createObjectURL(value);
|
|
428
|
+
localPreview = {
|
|
429
|
+
dataURI,
|
|
430
|
+
orientation,
|
|
431
|
+
source: 'local'
|
|
432
|
+
};
|
|
433
|
+
} catch (e) {
|
|
434
|
+
throw new LocalPreviewError('local-preview-image', e instanceof Error ? e : undefined);
|
|
435
|
+
}
|
|
436
|
+
break;
|
|
437
|
+
case 'video':
|
|
438
|
+
try {
|
|
439
|
+
const dataURI = await takeSnapshot(value);
|
|
440
|
+
localPreview = {
|
|
441
|
+
dataURI,
|
|
442
|
+
orientation: 1,
|
|
443
|
+
source: 'local'
|
|
444
|
+
};
|
|
445
|
+
} catch (e) {
|
|
446
|
+
throw new LocalPreviewError('local-preview-video', e instanceof Error ? e : undefined);
|
|
447
|
+
}
|
|
448
|
+
break;
|
|
449
|
+
default:
|
|
450
|
+
throw new LocalPreviewError('local-preview-unsupported');
|
|
451
|
+
}
|
|
452
|
+
} else {
|
|
453
|
+
throw new LocalPreviewError('local-preview-unsupported');
|
|
454
|
+
}
|
|
455
|
+
const preview = {
|
|
456
|
+
...localPreview,
|
|
457
|
+
dimensions
|
|
458
|
+
};
|
|
459
|
+
let source;
|
|
460
|
+
switch (preview.source) {
|
|
461
|
+
case 'local':
|
|
462
|
+
source = 'cache-local';
|
|
463
|
+
break;
|
|
464
|
+
case 'remote':
|
|
465
|
+
source = 'cache-remote';
|
|
466
|
+
break;
|
|
467
|
+
case 'ssr-server':
|
|
468
|
+
source = 'cache-ssr-server';
|
|
469
|
+
break;
|
|
470
|
+
case 'ssr-client':
|
|
471
|
+
source = 'cache-ssr-client';
|
|
472
|
+
break;
|
|
473
|
+
default:
|
|
474
|
+
source = preview.source;
|
|
475
|
+
}
|
|
476
|
+
// We want to embed some meta context into dataURI for Copy/Paste to work.
|
|
477
|
+
const dataURI = mediaBlobUrlAttrs ? addFileAttrsToUrl(preview.dataURI, mediaBlobUrlAttrs) : preview.dataURI;
|
|
478
|
+
// We store new cardPreview into cache
|
|
479
|
+
cardPreviewCache.set(identifier.id, mode, {
|
|
480
|
+
...preview,
|
|
481
|
+
source,
|
|
482
|
+
dataURI
|
|
483
|
+
});
|
|
484
|
+
setCardPreview({
|
|
485
|
+
...preview,
|
|
486
|
+
dataURI
|
|
487
|
+
});
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
} catch (e) {
|
|
491
|
+
/**
|
|
492
|
+
* We report the error if:
|
|
493
|
+
* - local preview is supported and fails
|
|
494
|
+
* - local preview is unsupported and remote preview is NOT READY
|
|
495
|
+
* i.e. the function was called for "no reason".
|
|
496
|
+
* We DON'T report the error if:
|
|
497
|
+
* - local preview is unsupported and remote preview IS READY
|
|
498
|
+
* i.e. local preview is available and not supported,
|
|
499
|
+
* but we are after the remote preview instead.
|
|
500
|
+
*/
|
|
501
|
+
if (!isUnsupportedLocalPreviewError(e) || isUnsupportedLocalPreviewError(e) && !isRemotePreviewReady) {
|
|
502
|
+
fireNonCriticalErrorEventRef.current && fireNonCriticalErrorEventRef.current(e);
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* No matter the reason why the local preview failed, we break the process
|
|
506
|
+
* if there is no remote preview available
|
|
507
|
+
*/
|
|
508
|
+
if (!isRemotePreviewReady) {
|
|
509
|
+
throw e;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
if (!isRemotePreviewReady) {
|
|
513
|
+
/**
|
|
514
|
+
* We throw this in case this function has been called
|
|
515
|
+
* without checking isRemotePreviewReady first.
|
|
516
|
+
* If remote preview is not ready, the call to getCardPreviewFromBackend
|
|
517
|
+
* will generate a console error due to a 404 code
|
|
518
|
+
*/
|
|
519
|
+
throw new MediaCardError('remote-preview-not-ready');
|
|
520
|
+
}
|
|
521
|
+
const remotePreview = await fetchAndCacheRemotePreview(mediaClient, identifier.id, dimensions !== null && dimensions !== void 0 ? dimensions : {}, imageURLParams, mediaBlobUrlAttrs, traceContext);
|
|
522
|
+
setCardPreview(remotePreview);
|
|
523
|
+
return;
|
|
524
|
+
} catch (e) {
|
|
525
|
+
const wrappedError = ensureMediaCardError('preview-fetch', e);
|
|
526
|
+
// If remote preview fails, we set status 'error'
|
|
527
|
+
// If local preview fails (i.e, no remote preview available),
|
|
528
|
+
// we can stay in the same status until there is a remote preview available
|
|
529
|
+
// If it's any other error we set status 'error'
|
|
530
|
+
if (isLocalPreviewError(wrappedError)) {
|
|
531
|
+
// This error should already been logged inside the getCardPreview. No need to log it here.
|
|
532
|
+
setIsBannedLocalPreview(true);
|
|
533
|
+
} else {
|
|
534
|
+
if (!['complete', 'error', 'failed-processing'].includes(status)) {
|
|
535
|
+
setStatus('error');
|
|
536
|
+
setError(wrappedError);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
//----------------------------------------------------------------//
|
|
543
|
+
//------------ resolveUpfrontPreview useEffect -------------------//
|
|
544
|
+
//----------------------------------------------------------------//
|
|
545
|
+
const prevCardPreview = usePrevious(cardPreview);
|
|
546
|
+
const dimensionsRef = useCurrentValueRef(dimensions);
|
|
547
|
+
useEffect(() => {
|
|
548
|
+
const resolveUpfrontPreview = async identifier => {
|
|
549
|
+
// We block any possible future call to this method regardless of the outcome (success or fail)
|
|
550
|
+
// If it fails, the normal preview fetch should occur after the file state is fetched anyways
|
|
551
|
+
wasResolvedUpfrontPreviewRef.current = true;
|
|
552
|
+
try {
|
|
553
|
+
const requestedDimensions = {
|
|
554
|
+
...dimensions
|
|
555
|
+
};
|
|
556
|
+
const newCardPreview = await fetchRemotePreviewRef.current(identifier);
|
|
557
|
+
const areValidRequestedDimensions = !isBigger(requestedDimensions, dimensionsRef.current);
|
|
558
|
+
|
|
559
|
+
// If there are new and bigger dimensions in the props, and the upfront preview is still resolving,
|
|
560
|
+
// the fetched preview is no longer valid, and thus, we dismiss it
|
|
561
|
+
if (areValidRequestedDimensions) {
|
|
562
|
+
setCardPreview(newCardPreview);
|
|
563
|
+
}
|
|
564
|
+
} catch (e) {
|
|
565
|
+
// NO need to log error. If this call fails, a refetch will happen after
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
const hadSSRCardPreview = ssr === 'client' && !!prevCardPreview && isSSRClientPreview(prevCardPreview);
|
|
569
|
+
if ((isCardVisible || hadSSRCardPreview) && !cardPreview && !wasResolvedUpfrontPreviewRef.current) {
|
|
570
|
+
resolveUpfrontPreview(identifier);
|
|
571
|
+
}
|
|
572
|
+
}, [cardPreview, dimensions, dimensionsRef, fetchRemotePreviewRef, identifier, isCardVisible, prevCardPreview, ssr]);
|
|
573
|
+
|
|
574
|
+
//----------------------------------------------------------------//
|
|
575
|
+
//------------------------ handle fireCopiedEvent --------------//
|
|
576
|
+
//----------------------------------------------------------------//
|
|
577
|
+
|
|
578
|
+
useEffect(() => {
|
|
579
|
+
var _getDocument;
|
|
580
|
+
const fireCopiedCardEvent = () => {
|
|
581
|
+
cardElement && createAnalyticsEvent && fireCopiedEvent(createAnalyticsEvent, metadata.id, cardElement);
|
|
582
|
+
};
|
|
583
|
+
|
|
584
|
+
// we add a listener for each of the cards on the page
|
|
585
|
+
// and then check if the triggered listener is from the card
|
|
586
|
+
// that contains a div in current window.getSelection()
|
|
587
|
+
// won't work in IE11
|
|
588
|
+
(_getDocument = getDocument()) === null || _getDocument === void 0 ? void 0 : _getDocument.addEventListener('copy', fireCopiedCardEvent);
|
|
589
|
+
return () => {
|
|
590
|
+
var _getDocument2;
|
|
591
|
+
(_getDocument2 = getDocument()) === null || _getDocument2 === void 0 ? void 0 : _getDocument2.removeEventListener('copy', fireCopiedCardEvent);
|
|
592
|
+
};
|
|
593
|
+
}, [cardElement, createAnalyticsEvent, metadata.id]);
|
|
594
|
+
|
|
595
|
+
//----------------------------------------------------------------//
|
|
596
|
+
//----------------- update status flags --------------------------//
|
|
597
|
+
//----------------------------------------------------------------//
|
|
598
|
+
|
|
599
|
+
useEffect(() => {
|
|
600
|
+
if ((fileStateValue === null || fileStateValue === void 0 ? void 0 : fileStateValue.status) === 'processing') {
|
|
601
|
+
fileStateFlagsRef.current.wasStatusProcessing = true;
|
|
602
|
+
} else if ((fileStateValue === null || fileStateValue === void 0 ? void 0 : fileStateValue.status) === 'uploading') {
|
|
603
|
+
fileStateFlagsRef.current.wasStatusUploading = true;
|
|
604
|
+
}
|
|
605
|
+
}, [fileStateValue === null || fileStateValue === void 0 ? void 0 : fileStateValue.status]);
|
|
606
|
+
|
|
607
|
+
//----------------------------------------------------------------//
|
|
608
|
+
//---------------- fetch and resolve card preview ----------------//
|
|
609
|
+
//----------------------------------------------------------------//
|
|
610
|
+
|
|
611
|
+
const prevDimensions = usePrevious(dimensions);
|
|
612
|
+
const prevIsCardVisible = usePrevious(isCardVisible);
|
|
613
|
+
const prevStatus = usePrevious(status);
|
|
614
|
+
useEffect(() => {
|
|
615
|
+
if (prevStatus !== undefined && status !== prevStatus) {
|
|
616
|
+
fireOperationalEventRef.current();
|
|
617
|
+
}
|
|
618
|
+
}, [fireOperationalEventRef, prevStatus, status]);
|
|
619
|
+
useEffect(() => {
|
|
620
|
+
var _ssrDataRef$current3;
|
|
621
|
+
/**
|
|
622
|
+
* Variable turnedVisible should only be true when media card
|
|
623
|
+
* was invisible in the previous state and is visible in the current one
|
|
624
|
+
*
|
|
625
|
+
* prevIsCardVisible | isCardVisible | turnedVisible
|
|
626
|
+
* ----------------------------------------------------
|
|
627
|
+
* false | false | false
|
|
628
|
+
* false | true | true
|
|
629
|
+
* true | true | false
|
|
630
|
+
* true | false | false (unreachable case)
|
|
631
|
+
* ----------------------------------------------------
|
|
632
|
+
*/
|
|
633
|
+
|
|
634
|
+
const turnedVisible = !prevIsCardVisible && isCardVisible;
|
|
635
|
+
if (turnedVisible) {
|
|
636
|
+
fireCommencedEventRef.current();
|
|
637
|
+
}
|
|
638
|
+
if (cardPreview && turnedVisible && isSSRDataPreview(cardPreview) && isBigger((_ssrDataRef$current3 = ssrDataRef.current) === null || _ssrDataRef$current3 === void 0 ? void 0 : _ssrDataRef$current3.dimensions, dimensions)) {
|
|
639
|
+
// If dimensions from Server have changed and are bigger,
|
|
640
|
+
// we need to refetch
|
|
641
|
+
// refetchSRRPreview
|
|
642
|
+
fetchRemotePreviewRef.current(identifier).then(setCardPreview).catch(e => {
|
|
643
|
+
const wrappedError = ensureMediaCardError('remote-preview-fetch-ssr', e, true);
|
|
644
|
+
fireNonCriticalErrorEventRef.current(wrappedError);
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
if (fileStateValue && shouldResolvePreview({
|
|
648
|
+
status,
|
|
649
|
+
fileState: fileStateValue,
|
|
650
|
+
prevDimensions,
|
|
651
|
+
dimensions,
|
|
652
|
+
hasCardPreview: !!cardPreview,
|
|
653
|
+
isBannedLocalPreview,
|
|
654
|
+
wasResolvedUpfrontPreview: wasResolvedUpfrontPreviewRef.current
|
|
655
|
+
})) {
|
|
656
|
+
resolvePreviewRef.current(identifier, fileStateValue);
|
|
657
|
+
}
|
|
658
|
+
if (turnedVisible && ssr && !!cardPreview && isSSRClientPreview(cardPreview)) {
|
|
659
|
+
// Since the SSR preview brings the token in the query params,
|
|
660
|
+
// We need to fetch the remote preview to be able to cache it,
|
|
661
|
+
fetchRemotePreviewRef.current(identifier).catch(() => {
|
|
662
|
+
// No need to log this error.
|
|
663
|
+
// If preview fails, it will be refetched later
|
|
664
|
+
//TODO: test this catch
|
|
665
|
+
// https://product-fabric.atlassian.net/browse/MEX-1071
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
}, [cardPreview, dimensions, fetchRemotePreviewRef, fileStateValue, fireCommencedEventRef, fireNonCriticalErrorEventRef, identifier, isBannedLocalPreview, isCardVisible, prevDimensions, prevIsCardVisible, resolvePreviewRef, ssr, status]);
|
|
669
|
+
|
|
670
|
+
//----------------------------------------------------------------//
|
|
671
|
+
//----------------- set complete status --------------------------//
|
|
672
|
+
//----------------------------------------------------------------//
|
|
673
|
+
|
|
674
|
+
useEffect(() => {
|
|
675
|
+
if (previewDidRender &&
|
|
676
|
+
// We should't complete if status is uploading
|
|
677
|
+
['loading-preview', 'processing'].includes(status)) {
|
|
678
|
+
setStatus('complete');
|
|
679
|
+
// TODO MEX-788: add test for "do not remove the card preview when unsubscribing".
|
|
680
|
+
setIsBannedLocalPreview(false);
|
|
681
|
+
}
|
|
682
|
+
}, [previewDidRender, status]);
|
|
683
|
+
|
|
684
|
+
//----------------------------------------------------------------//
|
|
685
|
+
//----------------- set isPlayingFile state ----------------------//
|
|
686
|
+
//----------------------------------------------------------------//
|
|
687
|
+
|
|
688
|
+
useEffect(() => {
|
|
689
|
+
const isVideo = fileAttributes.fileMediatype === 'video';
|
|
690
|
+
const {
|
|
691
|
+
mimeType
|
|
692
|
+
} = getFileDetails(identifier, fileStateValue);
|
|
693
|
+
const isVideoPlayable = videoIsPlayable(isBannedLocalPreview, fileStateValue, mimeType);
|
|
694
|
+
if (isVideo && !isPlayingFile && disableOverlay && useInlinePlayer && isVideoPlayable) {
|
|
695
|
+
setIsPlayingFile(true);
|
|
696
|
+
}
|
|
697
|
+
}, [disableOverlay, fileAttributes.fileMediatype, fileStateValue, identifier, isBannedLocalPreview, isPlayingFile, useInlinePlayer]);
|
|
698
|
+
|
|
699
|
+
//----------------------------------------------------------------//
|
|
700
|
+
//----------------- fireScreenEvent ------------------------------//
|
|
701
|
+
//----------------------------------------------------------------//
|
|
702
|
+
|
|
703
|
+
useEffect(() => {
|
|
704
|
+
if (prevStatus !== undefined && status !== prevStatus) {
|
|
705
|
+
if (status === 'complete' || fileAttributes.fileMediatype === 'video' && !!cardPreview && status === 'processing') {
|
|
706
|
+
fireScreenEventRef.current();
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
}, [status, prevStatus, fileAttributes, cardPreview, fireScreenEventRef]);
|
|
710
|
+
|
|
711
|
+
//----------------------------------------------------------------//
|
|
712
|
+
//----------------- abort UFO experience -------------------------//
|
|
713
|
+
//----------------------------------------------------------------//
|
|
714
|
+
|
|
715
|
+
useEffect(() => {
|
|
716
|
+
return () => {
|
|
717
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
718
|
+
fireAbortedEventRef.current();
|
|
719
|
+
};
|
|
720
|
+
}, [fireAbortedEventRef]);
|
|
721
|
+
|
|
722
|
+
//----------------------------------------------------------------//
|
|
723
|
+
//------------------ Subscribe to file state ---------------------//
|
|
724
|
+
//----------------------------------------------------------------//
|
|
725
|
+
|
|
726
|
+
const updateFileStateRef = useCurrentValueRef(() => {
|
|
727
|
+
if (fileState) {
|
|
728
|
+
// do not update the card status if the status is final
|
|
729
|
+
if (['complete', 'error', 'failed-processing'].includes(status)) {
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
if (fileState.status !== 'error') {
|
|
733
|
+
const mediaType = 'mediaType' in fileState ? fileState.mediaType : undefined;
|
|
734
|
+
const isPreviewable = !!mediaType && ['audio', 'video', 'image', 'doc'].indexOf(mediaType) > -1;
|
|
735
|
+
const isPreviewableFileState = !!fileState.preview;
|
|
736
|
+
const isSupportedLocalPreview = mediaType === 'image' || mediaType === 'video';
|
|
737
|
+
const hasLocalPreview = !isBannedLocalPreview && isPreviewableFileState && isSupportedLocalPreview && !!fileState.mimeType && isMimeTypeSupportedByBrowser(fileState.mimeType);
|
|
738
|
+
const hasRemotePreview = isImageRepresentationReady(fileState);
|
|
739
|
+
const hasPreview = hasLocalPreview || hasRemotePreview;
|
|
740
|
+
let newStatus;
|
|
741
|
+
switch (fileState.status) {
|
|
742
|
+
case 'uploading':
|
|
743
|
+
case 'failed-processing':
|
|
744
|
+
case 'processing':
|
|
745
|
+
newStatus = fileState.status;
|
|
746
|
+
break;
|
|
747
|
+
case 'processed':
|
|
748
|
+
if (!isPreviewable || !hasPreview) {
|
|
749
|
+
newStatus = 'complete';
|
|
750
|
+
break;
|
|
751
|
+
}
|
|
752
|
+
newStatus = 'loading-preview';
|
|
753
|
+
break;
|
|
754
|
+
default:
|
|
755
|
+
newStatus = 'loading';
|
|
756
|
+
}
|
|
757
|
+
const newProgress = newStatus === 'uploading' && fileState.status === 'uploading' ? fileState.progress : 1;
|
|
758
|
+
setStatus(newStatus);
|
|
759
|
+
uploadProgressRef.current = newProgress;
|
|
760
|
+
} else {
|
|
761
|
+
const e = new MediaFileStateError(fileState.id, fileState.reason, fileState.message, fileState.details);
|
|
762
|
+
const errorReason = status === 'uploading' ? 'upload' : 'metadata-fetch';
|
|
763
|
+
setError(new MediaCardError(errorReason, e));
|
|
764
|
+
setStatus('error');
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
});
|
|
768
|
+
useEffect(() => {
|
|
769
|
+
updateFileStateRef.current();
|
|
770
|
+
}, [fileState, updateFileStateRef]);
|
|
771
|
+
|
|
772
|
+
//----------------------------------------------------------------//
|
|
773
|
+
//---------------------- Render Card Function --------------------//
|
|
774
|
+
//----------------------------------------------------------------//
|
|
775
|
+
|
|
776
|
+
const renderCard = (withCallbacks = true, cardStatusOverride, izLazyOverride) => {
|
|
777
|
+
const {
|
|
778
|
+
mediaItemType
|
|
779
|
+
} = identifier;
|
|
780
|
+
const isLazyWithOverride = izLazyOverride === undefined ? isLazy : izLazyOverride;
|
|
781
|
+
|
|
782
|
+
// Card can be artificially turned visible before entering the viewport
|
|
783
|
+
// For example, when we have the image in cache
|
|
784
|
+
const nativeLazyLoad = isLazyWithOverride && !isCardVisible;
|
|
785
|
+
// Force Media Image to always display img for SSR
|
|
786
|
+
const forceSyncDisplay = !!ssr;
|
|
787
|
+
const mediaCardCursor = getMediaCardCursor(!!useInlinePlayer, !!shouldOpenMediaViewer, status === 'error' || status === 'failed-processing', !!cardPreview, metadata.mediaType);
|
|
788
|
+
const card = /*#__PURE__*/React.createElement(CardViewV2, {
|
|
789
|
+
status: cardStatusOverride || status,
|
|
790
|
+
error: error,
|
|
791
|
+
mediaItemType: mediaItemType,
|
|
792
|
+
metadata: metadata,
|
|
793
|
+
cardPreview: cardPreview,
|
|
794
|
+
alt: alt,
|
|
795
|
+
appearance: appearance,
|
|
796
|
+
resizeMode: resizeMode,
|
|
797
|
+
dimensions: dimensions,
|
|
798
|
+
actions: computedActions,
|
|
799
|
+
selectable: selectable,
|
|
800
|
+
selected: selected,
|
|
801
|
+
onClick: withCallbacks ? onCardViewClick : undefined,
|
|
802
|
+
onMouseEnter: withCallbacks ? event => {
|
|
803
|
+
onMouseEnter === null || onMouseEnter === void 0 ? void 0 : onMouseEnter({
|
|
804
|
+
event,
|
|
805
|
+
mediaItemDetails: metadata
|
|
806
|
+
});
|
|
807
|
+
} : undefined,
|
|
808
|
+
disableOverlay: disableOverlay,
|
|
809
|
+
progress: uploadProgressRef.current,
|
|
810
|
+
onDisplayImage: withCallbacks ? () => {
|
|
811
|
+
const payloadPart = {
|
|
812
|
+
fileId: identifier.id,
|
|
813
|
+
isUserCollection: identifier.collectionName === RECENTS_COLLECTION
|
|
814
|
+
};
|
|
815
|
+
globalMediaEventEmitter.emit('media-viewed', {
|
|
816
|
+
viewingLevel: 'minimal',
|
|
817
|
+
...payloadPart
|
|
818
|
+
});
|
|
819
|
+
} : undefined,
|
|
820
|
+
innerRef: setCardElement,
|
|
821
|
+
testId: testId,
|
|
822
|
+
featureFlags: featureFlags,
|
|
823
|
+
titleBoxBgColor: titleBoxBgColor,
|
|
824
|
+
titleBoxIcon: titleBoxIcon,
|
|
825
|
+
onImageError: withCallbacks ? onImageError : undefined,
|
|
826
|
+
onImageLoad: withCallbacks ? onImageLoad : undefined,
|
|
827
|
+
nativeLazyLoad: nativeLazyLoad,
|
|
828
|
+
forceSyncDisplay: forceSyncDisplay,
|
|
829
|
+
mediaCardCursor: mediaCardCursor,
|
|
830
|
+
shouldHideTooltip: shouldHideTooltip
|
|
831
|
+
});
|
|
832
|
+
return isLazyWithOverride ? /*#__PURE__*/React.createElement(ViewportDetector, {
|
|
833
|
+
cardEl: cardElement,
|
|
834
|
+
onVisible: () => {
|
|
835
|
+
setIsCardVisible(true);
|
|
836
|
+
}
|
|
837
|
+
}, card) : card;
|
|
838
|
+
};
|
|
839
|
+
|
|
840
|
+
//----------------------------------------------------------------//
|
|
841
|
+
//-------------------------- RENDER ------------------------------//
|
|
842
|
+
//----------------------------------------------------------------//
|
|
843
|
+
|
|
844
|
+
const inlinePlayerFallback = renderCard(false, 'loading', false);
|
|
845
|
+
const collectionName = identifier.collectionName || '';
|
|
846
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, isPlayingFile ? /*#__PURE__*/React.createElement(Suspense, {
|
|
847
|
+
fallback: inlinePlayerFallback
|
|
848
|
+
}, /*#__PURE__*/React.createElement(InlinePlayerLazyV2, {
|
|
849
|
+
dimensions: dimensions,
|
|
850
|
+
originalDimensions: originalDimensions,
|
|
851
|
+
identifier: identifier,
|
|
852
|
+
autoplay: !!shouldAutoplay,
|
|
853
|
+
onFullscreenChange: onFullscreenChange,
|
|
854
|
+
onError: () => {
|
|
855
|
+
setIsPlayingFile(false);
|
|
856
|
+
},
|
|
857
|
+
onClick: onCardClick,
|
|
858
|
+
selected: selected,
|
|
859
|
+
ref: setCardElement,
|
|
860
|
+
testId: testId,
|
|
861
|
+
cardPreview: cardPreview
|
|
862
|
+
})) : renderCard(), mediaViewerSelectedItem ? /*#__PURE__*/ReactDOM.createPortal( /*#__PURE__*/React.createElement(MediaViewer, {
|
|
863
|
+
collectionName: collectionName,
|
|
864
|
+
items: mediaViewerItems || [],
|
|
865
|
+
mediaClientConfig: mediaClient.config,
|
|
866
|
+
selectedItem: mediaViewerSelectedItem,
|
|
867
|
+
onClose: () => {
|
|
868
|
+
setMediaViewerSelectedItem(null);
|
|
869
|
+
},
|
|
870
|
+
contextId: contextId,
|
|
871
|
+
featureFlags: featureFlags
|
|
872
|
+
}), document.body) : null, ssr === 'server' && /*#__PURE__*/React.createElement(StoreSSRDataScript, {
|
|
873
|
+
identifier: identifier,
|
|
874
|
+
dataURI: cardPreview === null || cardPreview === void 0 ? void 0 : cardPreview.dataURI,
|
|
875
|
+
dimensions: getRequestedDimensions({
|
|
876
|
+
dimensions,
|
|
877
|
+
element: cardElement
|
|
878
|
+
}),
|
|
879
|
+
error: ((_ssrReliabilityRef$cu = ssrReliabilityRef.current.server) === null || _ssrReliabilityRef$cu === void 0 ? void 0 : _ssrReliabilityRef$cu.status) === 'fail' ? ssrReliabilityRef.current.server : undefined
|
|
880
|
+
}));
|
|
881
|
+
};
|