@atlaskit/media-card 77.5.0 → 77.5.1
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 +6 -0
- package/dist/cjs/card/card.js +1 -1
- package/dist/cjs/card/media-card-analytics-error-boundary.js +1 -1
- package/dist/cjs/card/v2/cardV2.js +1 -1
- package/dist/cjs/card/v2/cardViewV2.js +7 -6
- package/dist/cjs/card/v2/useFilePreview/errors.js +132 -0
- package/dist/cjs/card/v2/useFilePreview/getPreview/cache.js +39 -0
- package/dist/cjs/card/v2/useFilePreview/getPreview/filePreviewStatus.js +45 -0
- package/dist/cjs/card/v2/useFilePreview/getPreview/getPreview.js +119 -0
- package/dist/cjs/card/v2/useFilePreview/getPreview/helpers.js +167 -0
- package/dist/cjs/card/v2/useFilePreview/getPreview/index.js +62 -0
- package/dist/cjs/card/v2/useFilePreview/getPreview/objectURLCache.js +85 -0
- package/dist/cjs/card/v2/useFilePreview/getPreview/videoSnapshot.js +58 -0
- package/dist/cjs/card/v2/useFilePreview/globalScope/getSSRData.js +14 -0
- package/dist/cjs/card/v2/useFilePreview/globalScope/globalScope.js +66 -0
- package/dist/cjs/card/v2/useFilePreview/globalScope/index.js +37 -0
- package/dist/cjs/card/v2/useFilePreview/globalScope/printScript.js +32 -0
- package/dist/cjs/card/v2/useFilePreview/globalScope/types.js +5 -0
- package/dist/cjs/card/v2/useFilePreview/helpers.js +64 -0
- package/dist/cjs/card/v2/useFilePreview/index.js +12 -0
- package/dist/cjs/card/v2/useFilePreview/types.js +5 -0
- package/dist/cjs/card/v2/{useFilePreview.js → useFilePreview/useFilePreview.js} +58 -194
- package/dist/cjs/inline/loader.js +1 -1
- package/dist/cjs/utils/ufoExperiences.js +1 -1
- package/dist/es2019/card/card.js +1 -1
- package/dist/es2019/card/media-card-analytics-error-boundary.js +1 -1
- package/dist/es2019/card/v2/cardV2.js +1 -1
- package/dist/es2019/card/v2/cardViewV2.js +7 -6
- package/dist/es2019/card/v2/useFilePreview/errors.js +81 -0
- package/dist/es2019/card/v2/useFilePreview/getPreview/cache.js +30 -0
- package/dist/es2019/card/v2/useFilePreview/getPreview/filePreviewStatus.js +43 -0
- package/dist/es2019/card/v2/useFilePreview/getPreview/getPreview.js +75 -0
- package/dist/es2019/card/v2/useFilePreview/getPreview/helpers.js +76 -0
- package/dist/es2019/card/v2/useFilePreview/getPreview/index.js +3 -0
- package/dist/es2019/card/v2/useFilePreview/getPreview/objectURLCache.js +44 -0
- package/dist/es2019/card/v2/useFilePreview/getPreview/videoSnapshot.js +36 -0
- package/dist/es2019/card/v2/useFilePreview/globalScope/getSSRData.js +8 -0
- package/dist/es2019/card/v2/useFilePreview/globalScope/globalScope.js +48 -0
- package/dist/es2019/card/v2/useFilePreview/globalScope/index.js +2 -0
- package/dist/es2019/card/v2/useFilePreview/globalScope/printScript.js +16 -0
- package/dist/es2019/card/v2/useFilePreview/globalScope/types.js +1 -0
- package/dist/es2019/card/v2/useFilePreview/helpers.js +61 -0
- package/dist/es2019/card/v2/useFilePreview/index.js +1 -0
- package/dist/es2019/card/v2/useFilePreview/types.js +1 -0
- package/dist/es2019/card/v2/{useFilePreview.js → useFilePreview/useFilePreview.js} +18 -132
- package/dist/es2019/inline/loader.js +1 -1
- package/dist/es2019/utils/ufoExperiences.js +1 -1
- package/dist/esm/card/card.js +1 -1
- package/dist/esm/card/media-card-analytics-error-boundary.js +1 -1
- package/dist/esm/card/v2/cardV2.js +1 -1
- package/dist/esm/card/v2/cardViewV2.js +7 -6
- package/dist/esm/card/v2/useFilePreview/errors.js +124 -0
- package/dist/esm/card/v2/useFilePreview/getPreview/cache.js +32 -0
- package/dist/esm/card/v2/useFilePreview/getPreview/filePreviewStatus.js +40 -0
- package/dist/esm/card/v2/useFilePreview/getPreview/getPreview.js +112 -0
- package/dist/esm/card/v2/useFilePreview/getPreview/helpers.js +160 -0
- package/dist/esm/card/v2/useFilePreview/getPreview/index.js +3 -0
- package/dist/esm/card/v2/useFilePreview/getPreview/objectURLCache.js +78 -0
- package/dist/esm/card/v2/useFilePreview/getPreview/videoSnapshot.js +51 -0
- package/dist/esm/card/v2/useFilePreview/globalScope/getSSRData.js +8 -0
- package/dist/esm/card/v2/useFilePreview/globalScope/globalScope.js +56 -0
- package/dist/esm/card/v2/useFilePreview/globalScope/index.js +2 -0
- package/dist/esm/card/v2/useFilePreview/globalScope/printScript.js +25 -0
- package/dist/esm/card/v2/useFilePreview/globalScope/types.js +1 -0
- package/dist/esm/card/v2/useFilePreview/helpers.js +57 -0
- package/dist/esm/card/v2/useFilePreview/index.js +1 -0
- package/dist/esm/card/v2/useFilePreview/types.js +1 -0
- package/dist/esm/card/v2/{useFilePreview.js → useFilePreview/useFilePreview.js} +41 -177
- package/dist/esm/inline/loader.js +1 -1
- package/dist/esm/utils/ufoExperiences.js +1 -1
- package/dist/types/card/v2/cardViewV2.d.ts +5 -4
- package/dist/types/card/v2/useFilePreview/errors.d.ts +38 -0
- package/dist/types/card/v2/useFilePreview/getPreview/cache.d.ts +21 -0
- package/dist/types/card/v2/useFilePreview/getPreview/filePreviewStatus.d.ts +4 -0
- package/dist/types/card/v2/useFilePreview/getPreview/getPreview.d.ts +9 -0
- package/dist/types/card/v2/useFilePreview/getPreview/helpers.d.ts +10 -0
- package/dist/types/card/v2/useFilePreview/getPreview/index.d.ts +3 -0
- package/dist/types/card/v2/useFilePreview/getPreview/objectURLCache.d.ts +12 -0
- package/dist/types/card/v2/useFilePreview/getPreview/videoSnapshot.d.ts +1 -0
- package/dist/types/card/v2/useFilePreview/globalScope/getSSRData.d.ts +3 -0
- package/dist/types/card/v2/useFilePreview/globalScope/globalScope.d.ts +15 -0
- package/dist/types/card/v2/useFilePreview/globalScope/index.d.ts +4 -0
- package/dist/types/card/v2/useFilePreview/globalScope/printScript.d.ts +2 -0
- package/dist/types/card/v2/useFilePreview/globalScope/types.d.ts +8 -0
- package/dist/types/card/v2/useFilePreview/helpers.d.ts +11 -0
- package/dist/types/card/v2/useFilePreview/index.d.ts +2 -0
- package/dist/types/card/v2/useFilePreview/types.d.ts +18 -0
- package/dist/{types-ts4.5/card/v2 → types/card/v2/useFilePreview}/useFilePreview.d.ts +8 -8
- package/dist/types-ts4.5/card/v2/cardViewV2.d.ts +5 -4
- package/dist/types-ts4.5/card/v2/useFilePreview/errors.d.ts +38 -0
- package/dist/types-ts4.5/card/v2/useFilePreview/getPreview/cache.d.ts +21 -0
- package/dist/types-ts4.5/card/v2/useFilePreview/getPreview/filePreviewStatus.d.ts +4 -0
- package/dist/types-ts4.5/card/v2/useFilePreview/getPreview/getPreview.d.ts +9 -0
- package/dist/types-ts4.5/card/v2/useFilePreview/getPreview/helpers.d.ts +10 -0
- package/dist/types-ts4.5/card/v2/useFilePreview/getPreview/index.d.ts +3 -0
- package/dist/types-ts4.5/card/v2/useFilePreview/getPreview/objectURLCache.d.ts +12 -0
- package/dist/types-ts4.5/card/v2/useFilePreview/getPreview/videoSnapshot.d.ts +1 -0
- package/dist/types-ts4.5/card/v2/useFilePreview/globalScope/getSSRData.d.ts +3 -0
- package/dist/types-ts4.5/card/v2/useFilePreview/globalScope/globalScope.d.ts +15 -0
- package/dist/types-ts4.5/card/v2/useFilePreview/globalScope/index.d.ts +4 -0
- package/dist/types-ts4.5/card/v2/useFilePreview/globalScope/printScript.d.ts +2 -0
- package/dist/types-ts4.5/card/v2/useFilePreview/globalScope/types.d.ts +8 -0
- package/dist/types-ts4.5/card/v2/useFilePreview/helpers.d.ts +11 -0
- package/dist/types-ts4.5/card/v2/useFilePreview/index.d.ts +2 -0
- package/dist/types-ts4.5/card/v2/useFilePreview/types.d.ts +18 -0
- package/dist/{types/card/v2 → types-ts4.5/card/v2/useFilePreview}/useFilePreview.d.ts +8 -8
- package/package.json +1 -1
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { isMimeTypeSupportedByBrowser } from '@atlaskit/media-common';
|
|
2
|
+
import { isPreviewableFileState, isPreviewableType, isImageRepresentationReady } from '@atlaskit/media-client';
|
|
3
|
+
import { isSupportedLocalPreview } from './helpers';
|
|
4
|
+
|
|
5
|
+
// TODO: align these checks with helpers from Media Client
|
|
6
|
+
// https://product-fabric.atlassian.net/browse/BMPT-1300
|
|
7
|
+
export const extractFilePreviewStatus = (fileState, isBannedLocalPreview) => {
|
|
8
|
+
const hasFilesize = 'size' in fileState && !!fileState.size;
|
|
9
|
+
const {
|
|
10
|
+
mediaType
|
|
11
|
+
} = 'mediaType' in fileState && fileState || {};
|
|
12
|
+
const {
|
|
13
|
+
mimeType
|
|
14
|
+
} = 'mimeType' in fileState && fileState || {};
|
|
15
|
+
const isPreviewable = !!mediaType && isPreviewableType(mediaType);
|
|
16
|
+
|
|
17
|
+
// Local preview is available only if it's supported by browser and supported by Media Card (isSupportedLocalPreview)
|
|
18
|
+
// For example, SVGs are mime type NOT supported by browser but media type supported by Media Card (image)
|
|
19
|
+
// Then, local Preview NOT available
|
|
20
|
+
const hasLocalPreview = !isBannedLocalPreview && isPreviewableFileState(fileState) && isSupportedLocalPreview(mediaType) && !!mimeType && isMimeTypeSupportedByBrowser(mimeType);
|
|
21
|
+
const hasRemotePreview = isImageRepresentationReady(fileState);
|
|
22
|
+
const hasPreview = hasLocalPreview || hasRemotePreview;
|
|
23
|
+
const isSupportedByBrowser = !!mimeType && isMimeTypeSupportedByBrowser(mimeType);
|
|
24
|
+
return {
|
|
25
|
+
hasFilesize,
|
|
26
|
+
isPreviewable,
|
|
27
|
+
hasPreview,
|
|
28
|
+
isSupportedByBrowser
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// CXP-2723 TODO: Review this in relation to removing status from the hook
|
|
33
|
+
export const isPreviewableStatus = (cardStatus, {
|
|
34
|
+
isPreviewable,
|
|
35
|
+
hasPreview,
|
|
36
|
+
isSupportedByBrowser
|
|
37
|
+
}) => {
|
|
38
|
+
return hasPreview && isPreviewable && (cardStatus === 'complete' || cardStatus === 'loading-preview' || cardStatus === 'uploading' ||
|
|
39
|
+
// For Video, we can have local or remote preview while processing.
|
|
40
|
+
// Then, we only want to show the thumbnail if the file is supported by the browser,
|
|
41
|
+
// this way we prevent playing unsupported videos that are not procesed
|
|
42
|
+
cardStatus === 'processing' && isSupportedByBrowser);
|
|
43
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { addFileAttrsToUrl } from '@atlaskit/media-client';
|
|
2
|
+
import { mediaFilePreviewCache } from './cache';
|
|
3
|
+
import { getLocalPreview, getRemotePreview } from './helpers';
|
|
4
|
+
import { SsrPreviewError } from '../errors';
|
|
5
|
+
const extendAndCachePreview = (id, mode, preview, mediaBlobUrlAttrs) => {
|
|
6
|
+
let source;
|
|
7
|
+
switch (preview.source) {
|
|
8
|
+
case 'local':
|
|
9
|
+
source = 'cache-local';
|
|
10
|
+
break;
|
|
11
|
+
case 'remote':
|
|
12
|
+
source = 'cache-remote';
|
|
13
|
+
break;
|
|
14
|
+
case 'ssr-server':
|
|
15
|
+
source = 'cache-ssr-server';
|
|
16
|
+
break;
|
|
17
|
+
case 'ssr-client':
|
|
18
|
+
source = 'cache-ssr-client';
|
|
19
|
+
break;
|
|
20
|
+
default:
|
|
21
|
+
source = preview.source;
|
|
22
|
+
}
|
|
23
|
+
// We want to embed some meta context into dataURI for Copy/Paste to work.
|
|
24
|
+
const dataURI = mediaBlobUrlAttrs ? addFileAttrsToUrl(preview.dataURI, mediaBlobUrlAttrs) : preview.dataURI;
|
|
25
|
+
// We store new cardPreview into cache
|
|
26
|
+
mediaFilePreviewCache.set(id, mode, {
|
|
27
|
+
...preview,
|
|
28
|
+
source,
|
|
29
|
+
dataURI
|
|
30
|
+
});
|
|
31
|
+
return {
|
|
32
|
+
...preview,
|
|
33
|
+
dataURI
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
export const getSSRCardPreview = (ssr, mediaClient, id, params, mediaBlobUrlAttrs) => {
|
|
37
|
+
let dataURI;
|
|
38
|
+
try {
|
|
39
|
+
const rawDataURI = mediaClient.getImageUrlSync(id, params);
|
|
40
|
+
// We want to embed some meta context into dataURI for Copy/Paste to work.
|
|
41
|
+
dataURI = mediaBlobUrlAttrs ? addFileAttrsToUrl(rawDataURI, mediaBlobUrlAttrs) : rawDataURI;
|
|
42
|
+
const source = ssr === 'client' ? 'ssr-client' : 'ssr-server';
|
|
43
|
+
return {
|
|
44
|
+
dataURI,
|
|
45
|
+
source,
|
|
46
|
+
orientation: 1
|
|
47
|
+
};
|
|
48
|
+
} catch (e) {
|
|
49
|
+
const reason = ssr === 'server' ? 'ssr-server-uri' : 'ssr-client-uri';
|
|
50
|
+
throw new SsrPreviewError(reason, e instanceof Error ? e : undefined);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
export const isLocalPreview = preview => {
|
|
54
|
+
const localSources = ['local', 'cache-local'];
|
|
55
|
+
return localSources.includes(preview.source);
|
|
56
|
+
};
|
|
57
|
+
export const isSSRClientPreview = preview => {
|
|
58
|
+
const ssrClientSources = ['ssr-client', 'cache-ssr-client'];
|
|
59
|
+
return ssrClientSources.includes(preview.source);
|
|
60
|
+
};
|
|
61
|
+
export const isSSRDataPreview = preview => preview.source === 'ssr-data';
|
|
62
|
+
export const getAndCacheRemotePreview = async (mediaClient, id, dimensions, params, mediaBlobUrlAttrs, traceContext) => {
|
|
63
|
+
const remotePreview = await getRemotePreview(mediaClient, id, params, traceContext);
|
|
64
|
+
return extendAndCachePreview(id, params.mode, {
|
|
65
|
+
...remotePreview,
|
|
66
|
+
dimensions
|
|
67
|
+
}, mediaBlobUrlAttrs);
|
|
68
|
+
};
|
|
69
|
+
export const getAndCacheLocalPreview = async (id, filePreview, dimensions, mode, mediaBlobUrlAttrs) => {
|
|
70
|
+
const localPreview = await getLocalPreview(filePreview);
|
|
71
|
+
return extendAndCachePreview(id, mode, {
|
|
72
|
+
...localPreview,
|
|
73
|
+
dimensions
|
|
74
|
+
}, mediaBlobUrlAttrs);
|
|
75
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { takeSnapshot } from './videoSnapshot';
|
|
2
|
+
import { getMediaTypeFromMimeType } from '@atlaskit/media-common';
|
|
3
|
+
import { getOrientation } from '@atlaskit/media-ui';
|
|
4
|
+
import { LocalPreviewError, RemotePreviewError } from '../errors';
|
|
5
|
+
/**
|
|
6
|
+
* This method tells the support for the media
|
|
7
|
+
* types covered in getCardPreviewFromFilePreview
|
|
8
|
+
*/
|
|
9
|
+
export const isSupportedLocalPreview = mediaType => mediaType === 'image' || mediaType === 'video';
|
|
10
|
+
const getImageLocalPreview = async value => {
|
|
11
|
+
try {
|
|
12
|
+
const orientation = await getOrientation(value);
|
|
13
|
+
const dataURI = URL.createObjectURL(value);
|
|
14
|
+
return {
|
|
15
|
+
dataURI,
|
|
16
|
+
orientation,
|
|
17
|
+
source: 'local'
|
|
18
|
+
};
|
|
19
|
+
} catch (e) {
|
|
20
|
+
throw new LocalPreviewError('local-preview-image', e instanceof Error ? e : undefined);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const getVideoLocalPreview = async value => {
|
|
24
|
+
try {
|
|
25
|
+
const dataURI = await takeSnapshot(value);
|
|
26
|
+
return {
|
|
27
|
+
dataURI,
|
|
28
|
+
orientation: 1,
|
|
29
|
+
source: 'local'
|
|
30
|
+
};
|
|
31
|
+
} catch (e) {
|
|
32
|
+
throw new LocalPreviewError('local-preview-video', e instanceof Error ? e : undefined);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
export const getLocalPreview = async filePreview => {
|
|
36
|
+
let value;
|
|
37
|
+
try {
|
|
38
|
+
const resolvedFilePreview = await filePreview;
|
|
39
|
+
value = resolvedFilePreview.value;
|
|
40
|
+
} catch (e) {
|
|
41
|
+
throw new LocalPreviewError('local-preview-rejected', e instanceof Error ? e : undefined);
|
|
42
|
+
}
|
|
43
|
+
if (typeof value === 'string') {
|
|
44
|
+
return {
|
|
45
|
+
dataURI: value,
|
|
46
|
+
orientation: 1,
|
|
47
|
+
source: 'local'
|
|
48
|
+
};
|
|
49
|
+
} else if (value instanceof Blob) {
|
|
50
|
+
const {
|
|
51
|
+
type
|
|
52
|
+
} = value;
|
|
53
|
+
const mediaType = getMediaTypeFromMimeType(type);
|
|
54
|
+
switch (mediaType) {
|
|
55
|
+
case 'image':
|
|
56
|
+
return getImageLocalPreview(value);
|
|
57
|
+
case 'video':
|
|
58
|
+
return getVideoLocalPreview(value);
|
|
59
|
+
default:
|
|
60
|
+
throw new LocalPreviewError('local-preview-unsupported');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
throw new LocalPreviewError('local-preview-unsupported');
|
|
64
|
+
};
|
|
65
|
+
export const getRemotePreview = async (mediaClient, id, params, traceContext) => {
|
|
66
|
+
try {
|
|
67
|
+
const blob = await mediaClient.getImage(id, params, undefined, undefined, traceContext);
|
|
68
|
+
return {
|
|
69
|
+
dataURI: URL.createObjectURL(blob),
|
|
70
|
+
orientation: 1,
|
|
71
|
+
source: 'remote'
|
|
72
|
+
};
|
|
73
|
+
} catch (e) {
|
|
74
|
+
throw new RemotePreviewError('remote-preview-fetch', e instanceof Error ? e : undefined);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { mediaFilePreviewCache } from './cache';
|
|
2
|
+
export { isPreviewableStatus, extractFilePreviewStatus } from './filePreviewStatus';
|
|
3
|
+
export { getSSRCardPreview, isLocalPreview, isSSRClientPreview, isSSRDataPreview, getAndCacheRemotePreview, getAndCacheLocalPreview } from './getPreview';
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { LRUMap } from 'lru_map';
|
|
2
|
+
import { EventEmitter2 } from 'eventemitter2';
|
|
3
|
+
export const PREVIEW_CACHE_LRU_SIZE = 50;
|
|
4
|
+
class ExtendedLRUCache extends LRUMap {
|
|
5
|
+
constructor(limit) {
|
|
6
|
+
super(limit);
|
|
7
|
+
this.eventEmitter = new EventEmitter2();
|
|
8
|
+
}
|
|
9
|
+
shift() {
|
|
10
|
+
const entry = super.shift();
|
|
11
|
+
this.eventEmitter.emit('shift', entry);
|
|
12
|
+
return entry;
|
|
13
|
+
}
|
|
14
|
+
on(event, callback) {
|
|
15
|
+
this.eventEmitter.on(event, callback);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export class ObjectURLCache {
|
|
19
|
+
constructor(size) {
|
|
20
|
+
this.cache = new ExtendedLRUCache(size);
|
|
21
|
+
this.cache.on('shift', entry => {
|
|
22
|
+
if (entry && entry[1].dataURI) {
|
|
23
|
+
URL.revokeObjectURL(entry[1].dataURI);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
has(key) {
|
|
28
|
+
return !!this.cache.find(key);
|
|
29
|
+
}
|
|
30
|
+
get(key) {
|
|
31
|
+
return this.cache.get(key);
|
|
32
|
+
}
|
|
33
|
+
set(key, value) {
|
|
34
|
+
this.cache.set(key, value);
|
|
35
|
+
}
|
|
36
|
+
remove(key) {
|
|
37
|
+
const removed = this.cache.delete(key);
|
|
38
|
+
removed && URL.revokeObjectURL(removed.dataURI);
|
|
39
|
+
}
|
|
40
|
+
clear() {
|
|
41
|
+
this.cache.clear();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export const createObjectURLCache = () => new ObjectURLCache(PREVIEW_CACHE_LRU_SIZE);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export const takeSnapshot = async blob => {
|
|
2
|
+
return new Promise((resolve, reject) => {
|
|
3
|
+
const url = URL.createObjectURL(blob);
|
|
4
|
+
const video = document.createElement('video');
|
|
5
|
+
video.preload = 'metadata';
|
|
6
|
+
video.src = url;
|
|
7
|
+
video.muted = true;
|
|
8
|
+
video.play().catch(() => {
|
|
9
|
+
return reject(new Error('failed to play video'));
|
|
10
|
+
});
|
|
11
|
+
video.addEventListener('timeupdate', function timeUpdateHandler() {
|
|
12
|
+
video.removeEventListener('timeupdate', timeUpdateHandler);
|
|
13
|
+
video.pause();
|
|
14
|
+
URL.revokeObjectURL(url);
|
|
15
|
+
//create canvas to draw our first frame on.
|
|
16
|
+
|
|
17
|
+
if (!video.videoWidth && !video.videoHeight) {
|
|
18
|
+
return reject(new Error('error retrieving video dimensions'));
|
|
19
|
+
}
|
|
20
|
+
const canvas = document.createElement('canvas');
|
|
21
|
+
canvas.width = video.videoWidth;
|
|
22
|
+
canvas.height = video.videoHeight;
|
|
23
|
+
const context = canvas.getContext('2d');
|
|
24
|
+
if (!context) {
|
|
25
|
+
return reject(new Error('error creating canvas context'));
|
|
26
|
+
}
|
|
27
|
+
context.drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
28
|
+
const dataURL = canvas.toDataURL('image/jpeg', 0.85);
|
|
29
|
+
resolve(dataURL);
|
|
30
|
+
});
|
|
31
|
+
video.addEventListener('error', () => {
|
|
32
|
+
reject(new Error('failed to load video'));
|
|
33
|
+
URL.revokeObjectURL(url);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { printFunctionCall, printScript } from './printScript';
|
|
2
|
+
// ----- WARNING -----
|
|
3
|
+
// This is a very sensitive fraction of code.
|
|
4
|
+
// Any changes to this file must be tested directly in product before merging.
|
|
5
|
+
// The scripts printed here might differ from what we observe in our internal tests
|
|
6
|
+
// due to minimification, for example.
|
|
7
|
+
export const GLOBAL_MEDIA_CARD_SSR = 'mediaCardSsr';
|
|
8
|
+
export const GLOBAL_MEDIA_NAMESPACE = '__MEDIA_INTERNAL';
|
|
9
|
+
export function getMediaGlobalScope(globalScope = window) {
|
|
10
|
+
// Must match GLOBAL_MEDIA_NAMESPACE. Can't reference the constant from here.
|
|
11
|
+
const namespace = '__MEDIA_INTERNAL';
|
|
12
|
+
if (!globalScope[namespace]) {
|
|
13
|
+
globalScope[namespace] = {};
|
|
14
|
+
}
|
|
15
|
+
return globalScope[namespace];
|
|
16
|
+
}
|
|
17
|
+
export function getMediaCardSSR(globalScope = window) {
|
|
18
|
+
const globalMedia = getMediaGlobalScope(globalScope);
|
|
19
|
+
// Must match GLOBAL_MEDIA_CARD_SSR. Can't reference the constant from here.
|
|
20
|
+
const key = 'mediaCardSsr';
|
|
21
|
+
if (!globalMedia[key]) {
|
|
22
|
+
globalMedia[key] = {};
|
|
23
|
+
}
|
|
24
|
+
return globalMedia[key];
|
|
25
|
+
}
|
|
26
|
+
const dashed = param => param ? `-${param}` : '';
|
|
27
|
+
export const getKey = ({
|
|
28
|
+
id,
|
|
29
|
+
collectionName,
|
|
30
|
+
occurrenceKey
|
|
31
|
+
}) => `${id}${dashed(collectionName)}${dashed(occurrenceKey)}`;
|
|
32
|
+
export const storeDataURI = (key, dataURI, dimensions, error, globalScope = window) => {
|
|
33
|
+
const mediaCardSsr = getMediaCardSSR(globalScope);
|
|
34
|
+
mediaCardSsr[key] = {
|
|
35
|
+
dataURI,
|
|
36
|
+
dimensions,
|
|
37
|
+
error
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
const generateScript = (identifier, dataURI, dimensions, error) => {
|
|
41
|
+
const functionCall = printFunctionCall(storeDataURI, getKey(identifier), dataURI, dimensions, error);
|
|
42
|
+
return printScript([getMediaCardSSR.toString(), getMediaGlobalScope.toString(), functionCall]);
|
|
43
|
+
};
|
|
44
|
+
export const generateScriptProps = (identifier, dataURI, dimensions, error) => ({
|
|
45
|
+
dangerouslySetInnerHTML: {
|
|
46
|
+
__html: generateScript(identifier, dataURI, dimensions, error)
|
|
47
|
+
}
|
|
48
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const printParam = param => {
|
|
2
|
+
if (typeof param === 'string') {
|
|
3
|
+
return `'${param}'`;
|
|
4
|
+
} else if (typeof param === 'object') {
|
|
5
|
+
return JSON.stringify(param);
|
|
6
|
+
} else if (param === undefined) {
|
|
7
|
+
return 'undefined';
|
|
8
|
+
}
|
|
9
|
+
return param;
|
|
10
|
+
};
|
|
11
|
+
const printParams = args => args.map(arg => printParam(arg)).join(',');
|
|
12
|
+
export const printFunctionCall = (fn, ...args) => `(${fn.toString()})(${printParams(args)});`;
|
|
13
|
+
export const printScript = statements => `(function(){
|
|
14
|
+
${statements.join(';')}
|
|
15
|
+
})();
|
|
16
|
+
`;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { useRef, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Checks if at least one of next dimensions is bigger than current
|
|
5
|
+
* If a single dimension is undefined, returns false
|
|
6
|
+
*/
|
|
7
|
+
export const isBigger = (current, next) => {
|
|
8
|
+
const {
|
|
9
|
+
width: currentWidth,
|
|
10
|
+
height: currentHeight
|
|
11
|
+
} = current || {};
|
|
12
|
+
const {
|
|
13
|
+
width: nextWidth,
|
|
14
|
+
height: nextHeight
|
|
15
|
+
} = next || {};
|
|
16
|
+
if (!!currentWidth && !!currentHeight && !!nextWidth && !!nextHeight) {
|
|
17
|
+
const nextIsWider = currentWidth < nextWidth;
|
|
18
|
+
const nextIsHigher = currentHeight < nextHeight;
|
|
19
|
+
return nextIsHigher || nextIsWider;
|
|
20
|
+
} else {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/** Verifies if the current screen is retina display */
|
|
26
|
+
function isRetina() {
|
|
27
|
+
const mediaQuery = '(-webkit-min-device-pixel-ratio: 1.5), (min--moz-device-pixel-ratio: 1.5), (-o-min-device-pixel-ratio: 3/2), (min-resolution: 1.5dppx)';
|
|
28
|
+
return window.devicePixelRatio > 1 || window.matchMedia && window.matchMedia(mediaQuery).matches;
|
|
29
|
+
}
|
|
30
|
+
export const createRequestDimensions = dimensions => {
|
|
31
|
+
if (!dimensions) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const retinaFactor = isRetina() ? 2 : 1;
|
|
35
|
+
const {
|
|
36
|
+
width,
|
|
37
|
+
height
|
|
38
|
+
} = dimensions;
|
|
39
|
+
const result = {};
|
|
40
|
+
if (width) {
|
|
41
|
+
result.width = width * retinaFactor;
|
|
42
|
+
}
|
|
43
|
+
if (height) {
|
|
44
|
+
result.height = height * retinaFactor;
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/** Stores the provided value in a */
|
|
50
|
+
export function useCurrentValueRef(value) {
|
|
51
|
+
const ref = useRef(value);
|
|
52
|
+
ref.current = value;
|
|
53
|
+
return ref;
|
|
54
|
+
}
|
|
55
|
+
export function usePrevious(value) {
|
|
56
|
+
const ref = useRef();
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
ref.current = value;
|
|
59
|
+
}, [value]);
|
|
60
|
+
return ref.current;
|
|
61
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useFilePreview } from './useFilePreview';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,18 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { imageResizeModeToFileImageMode, isImageRepresentationReady } from '@atlaskit/media-client';
|
|
2
2
|
import { MediaFileStateError, useFileState, useMediaClient } from '@atlaskit/media-client-react';
|
|
3
|
-
import {
|
|
4
|
-
import { getOrientation } from '@atlaskit/media-ui';
|
|
3
|
+
import { isMimeTypeSupportedByBrowser } from '@atlaskit/media-common';
|
|
5
4
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
6
|
-
import {
|
|
7
|
-
import { extractErrorInfo } from '
|
|
8
|
-
import { isBigger } from '
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { useCurrentValueRef } from '../../utils/useCurrentValueRef';
|
|
12
|
-
import { usePrevious } from '../../utils/usePrevious';
|
|
13
|
-
import { takeSnapshot } from '../../utils/videoSnapshot';
|
|
14
|
-
import { fetchAndCacheRemotePreview, getCardPreviewFromCache, getSSRCardPreview, isLocalPreview, isSSRClientPreview, isSSRDataPreview, removeCardPreviewFromCache, shouldResolvePreview } from '../getCardPreview';
|
|
15
|
-
import cardPreviewCache from '../getCardPreview/cache';
|
|
5
|
+
import { ensureMediaFilePreviewError, ImageLoadError, isLocalPreviewError, isUnsupportedLocalPreviewError, MediaFilePreviewError } from './errors';
|
|
6
|
+
import { extractErrorInfo } from '../../../utils/analytics';
|
|
7
|
+
import { isBigger, createRequestDimensions, useCurrentValueRef, usePrevious } from './helpers';
|
|
8
|
+
import { generateScriptProps, getSSRData } from './globalScope';
|
|
9
|
+
import { getAndCacheRemotePreview, getSSRCardPreview, isLocalPreview, isSSRClientPreview, isSSRDataPreview, isPreviewableStatus, extractFilePreviewStatus, mediaFilePreviewCache, getAndCacheLocalPreview } from './getPreview';
|
|
16
10
|
export const useFilePreview = ({
|
|
17
11
|
resizeMode = 'crop',
|
|
18
12
|
identifier,
|
|
@@ -72,7 +66,7 @@ export const useFilePreview = ({
|
|
|
72
66
|
id
|
|
73
67
|
} = identifier;
|
|
74
68
|
const fileImageMode = imageResizeModeToFileImageMode(resizeMode);
|
|
75
|
-
cardPreview =
|
|
69
|
+
cardPreview = mediaFilePreviewCache.get(id, fileImageMode);
|
|
76
70
|
if (!cardPreview && ssr) {
|
|
77
71
|
cardPreview = getSSRPreview(ssr, identifier, mediaClient);
|
|
78
72
|
}
|
|
@@ -95,104 +89,22 @@ export const useFilePreview = ({
|
|
|
95
89
|
//----------------------------------------------------------------//
|
|
96
90
|
|
|
97
91
|
const fetchRemotePreviewRef = useCurrentValueRef(identifier => {
|
|
98
|
-
return
|
|
92
|
+
return getAndCacheRemotePreview(mediaClient, identifier.id, requestDimensions || {}, imageURLParams, mediaBlobUrlAttrs, traceContext);
|
|
99
93
|
});
|
|
100
94
|
const resolvePreviewRef = useCurrentValueRef(async (identifier, fileState) => {
|
|
101
95
|
const filePreview = isBannedLocalPreview ? undefined : fileState.status !== 'error' && 'mimeType' in fileState && isMimeTypeSupportedByBrowser(fileState.mimeType) ? fileState.preview : undefined;
|
|
102
96
|
const isRemotePreviewReady = isImageRepresentationReady(fileState);
|
|
103
97
|
try {
|
|
104
98
|
const mode = imageURLParams.mode;
|
|
105
|
-
const cachedPreview =
|
|
99
|
+
const cachedPreview = mediaFilePreviewCache.get(identifier.id, mode);
|
|
106
100
|
const dimensionsAreBigger = isBigger(cachedPreview === null || cachedPreview === void 0 ? void 0 : cachedPreview.dimensions, requestDimensions);
|
|
107
101
|
if (cachedPreview && !dimensionsAreBigger) {
|
|
108
102
|
return cachedPreview;
|
|
109
103
|
}
|
|
110
|
-
let localPreview;
|
|
111
104
|
try {
|
|
112
105
|
if (filePreview) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const resolvedFilePreview = await filePreview;
|
|
116
|
-
value = resolvedFilePreview.value;
|
|
117
|
-
} catch (e) {
|
|
118
|
-
throw new LocalPreviewError('local-preview-rejected', e instanceof Error ? e : undefined);
|
|
119
|
-
}
|
|
120
|
-
if (typeof value === 'string') {
|
|
121
|
-
localPreview = {
|
|
122
|
-
dataURI: value,
|
|
123
|
-
orientation: 1,
|
|
124
|
-
source: 'local'
|
|
125
|
-
};
|
|
126
|
-
} else if (value instanceof Blob) {
|
|
127
|
-
const {
|
|
128
|
-
type
|
|
129
|
-
} = value;
|
|
130
|
-
const mediaType = getMediaTypeFromMimeType(type);
|
|
131
|
-
switch (mediaType) {
|
|
132
|
-
case 'image':
|
|
133
|
-
try {
|
|
134
|
-
const orientation = await getOrientation(value);
|
|
135
|
-
const dataURI = URL.createObjectURL(value);
|
|
136
|
-
localPreview = {
|
|
137
|
-
dataURI,
|
|
138
|
-
orientation,
|
|
139
|
-
source: 'local'
|
|
140
|
-
};
|
|
141
|
-
} catch (e) {
|
|
142
|
-
throw new LocalPreviewError('local-preview-image', e instanceof Error ? e : undefined);
|
|
143
|
-
}
|
|
144
|
-
break;
|
|
145
|
-
case 'video':
|
|
146
|
-
try {
|
|
147
|
-
const dataURI = await takeSnapshot(value);
|
|
148
|
-
localPreview = {
|
|
149
|
-
dataURI,
|
|
150
|
-
orientation: 1,
|
|
151
|
-
source: 'local'
|
|
152
|
-
};
|
|
153
|
-
} catch (e) {
|
|
154
|
-
throw new LocalPreviewError('local-preview-video', e instanceof Error ? e : undefined);
|
|
155
|
-
}
|
|
156
|
-
break;
|
|
157
|
-
default:
|
|
158
|
-
throw new LocalPreviewError('local-preview-unsupported');
|
|
159
|
-
}
|
|
160
|
-
} else {
|
|
161
|
-
throw new LocalPreviewError('local-preview-unsupported');
|
|
162
|
-
}
|
|
163
|
-
const preview = {
|
|
164
|
-
...localPreview,
|
|
165
|
-
dimensions: requestDimensions
|
|
166
|
-
};
|
|
167
|
-
let source;
|
|
168
|
-
switch (preview.source) {
|
|
169
|
-
case 'local':
|
|
170
|
-
source = 'cache-local';
|
|
171
|
-
break;
|
|
172
|
-
case 'remote':
|
|
173
|
-
source = 'cache-remote';
|
|
174
|
-
break;
|
|
175
|
-
case 'ssr-server':
|
|
176
|
-
source = 'cache-ssr-server';
|
|
177
|
-
break;
|
|
178
|
-
case 'ssr-client':
|
|
179
|
-
source = 'cache-ssr-client';
|
|
180
|
-
break;
|
|
181
|
-
default:
|
|
182
|
-
source = preview.source;
|
|
183
|
-
}
|
|
184
|
-
// We want to embed some meta context into dataURI for Copy/Paste to work.
|
|
185
|
-
const dataURI = mediaBlobUrlAttrs ? addFileAttrsToUrl(preview.dataURI, mediaBlobUrlAttrs) : preview.dataURI;
|
|
186
|
-
// We store new cardPreview into cache
|
|
187
|
-
cardPreviewCache.set(identifier.id, mode, {
|
|
188
|
-
...preview,
|
|
189
|
-
source,
|
|
190
|
-
dataURI
|
|
191
|
-
});
|
|
192
|
-
setCardPreview({
|
|
193
|
-
...preview,
|
|
194
|
-
dataURI
|
|
195
|
-
});
|
|
106
|
+
const localPreview = await getAndCacheLocalPreview(identifier.id, filePreview, requestDimensions || {}, mode, mediaBlobUrlAttrs);
|
|
107
|
+
setCardPreview(localPreview);
|
|
196
108
|
return;
|
|
197
109
|
}
|
|
198
110
|
} catch (e) {
|
|
@@ -225,13 +137,13 @@ export const useFilePreview = ({
|
|
|
225
137
|
* If remote preview is not ready, the call to getCardPreviewFromBackend
|
|
226
138
|
* will generate a console error due to a 404 code
|
|
227
139
|
*/
|
|
228
|
-
throw new
|
|
140
|
+
throw new MediaFilePreviewError('remote-preview-not-ready');
|
|
229
141
|
}
|
|
230
142
|
const remotePreview = await fetchRemotePreviewRef.current(identifier);
|
|
231
143
|
setCardPreview(remotePreview);
|
|
232
144
|
return;
|
|
233
145
|
} catch (e) {
|
|
234
|
-
const wrappedError =
|
|
146
|
+
const wrappedError = ensureMediaFilePreviewError('preview-fetch', e);
|
|
235
147
|
// If remote preview fails, we set status 'error'
|
|
236
148
|
// If local preview fails (i.e, no remote preview available),
|
|
237
149
|
// we can stay in the same status until there is a remote preview available
|
|
@@ -293,7 +205,7 @@ export const useFilePreview = ({
|
|
|
293
205
|
// refetchSRRPreview: If dimensions from Server have changed and are bigger,
|
|
294
206
|
// we need to refetch
|
|
295
207
|
fetchRemotePreviewRef.current(identifier).then(setCardPreview).catch(e => {
|
|
296
|
-
const wrappedError =
|
|
208
|
+
const wrappedError = ensureMediaFilePreviewError('remote-preview-fetch-ssr', e, true);
|
|
297
209
|
setNonCriticalError(wrappedError);
|
|
298
210
|
});
|
|
299
211
|
}
|
|
@@ -301,15 +213,7 @@ export const useFilePreview = ({
|
|
|
301
213
|
// Both compare dimensions, but the above is only for SSR in order to refetchSRRPreview and
|
|
302
214
|
// will swallow any errors. Below logic only applies when there is no card preview or the dimensions
|
|
303
215
|
// are bigger.
|
|
304
|
-
if (fileState &&
|
|
305
|
-
status,
|
|
306
|
-
fileState: fileState,
|
|
307
|
-
prevDimensions: prevRequestDimensions,
|
|
308
|
-
dimensions: requestDimensions,
|
|
309
|
-
hasCardPreview: !!cardPreview,
|
|
310
|
-
isBannedLocalPreview,
|
|
311
|
-
wasResolvedUpfrontPreview: wasResolvedUpfrontPreviewRef.current
|
|
312
|
-
})) {
|
|
216
|
+
if (fileState && wasResolvedUpfrontPreviewRef.current && isPreviewableStatus(status, extractFilePreviewStatus(fileState, isBannedLocalPreview)) && (!cardPreview || isBigger(prevRequestDimensions, requestDimensions))) {
|
|
313
217
|
resolvePreviewRef.current(identifier, fileState);
|
|
314
218
|
}
|
|
315
219
|
if (!skipRemote && ssr && !!cardPreview && isSSRClientPreview(cardPreview)) {
|
|
@@ -379,7 +283,7 @@ export const useFilePreview = ({
|
|
|
379
283
|
} else {
|
|
380
284
|
const e = new MediaFileStateError(fileState.id, fileState.reason, fileState.message, fileState.details);
|
|
381
285
|
const errorReason = status === 'uploading' ? 'upload' : 'metadata-fetch';
|
|
382
|
-
setError(new
|
|
286
|
+
setError(new MediaFilePreviewError(errorReason, e));
|
|
383
287
|
setStatus('error');
|
|
384
288
|
}
|
|
385
289
|
}
|
|
@@ -420,7 +324,7 @@ export const useFilePreview = ({
|
|
|
420
324
|
setNonCriticalError(error);
|
|
421
325
|
}
|
|
422
326
|
const fileImageMode = imageResizeModeToFileImageMode(resizeMode);
|
|
423
|
-
|
|
327
|
+
mediaFilePreviewCache.remove(identifier.id, fileImageMode);
|
|
424
328
|
setCardPreview(undefined);
|
|
425
329
|
} else {
|
|
426
330
|
if (!['complete', 'error', 'failed-processing'].includes(status)) {
|
|
@@ -479,24 +383,6 @@ export const useFilePreview = ({
|
|
|
479
383
|
getScriptProps
|
|
480
384
|
};
|
|
481
385
|
};
|
|
482
|
-
const createRequestDimensions = dimensions => {
|
|
483
|
-
if (!dimensions) {
|
|
484
|
-
return;
|
|
485
|
-
}
|
|
486
|
-
const retinaFactor = isRetina() ? 2 : 1;
|
|
487
|
-
const {
|
|
488
|
-
width,
|
|
489
|
-
height
|
|
490
|
-
} = dimensions;
|
|
491
|
-
const result = {};
|
|
492
|
-
if (width) {
|
|
493
|
-
result.width = width * retinaFactor;
|
|
494
|
-
}
|
|
495
|
-
if (height) {
|
|
496
|
-
result.height = height * retinaFactor;
|
|
497
|
-
}
|
|
498
|
-
return result;
|
|
499
|
-
};
|
|
500
386
|
const initialSsrReliability = {
|
|
501
387
|
server: {
|
|
502
388
|
status: 'unknown'
|
|
@@ -37,7 +37,7 @@ export default class MediaInlineCardLoader extends React.PureComponent {
|
|
|
37
37
|
} = this.state;
|
|
38
38
|
const analyticsContext = {
|
|
39
39
|
packageVersion: "@atlaskit/media-card",
|
|
40
|
-
packageName: "77.5.
|
|
40
|
+
packageName: "77.5.1",
|
|
41
41
|
componentName: 'mediaInlineCard',
|
|
42
42
|
component: 'mediaInlineCard'
|
|
43
43
|
};
|