@atlaskit/media-client 20.0.1 → 20.0.3

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 (161) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/cjs/client/collection-fetcher.js +4 -44
  3. package/dist/cjs/client/file-fetcher/error.js +3 -19
  4. package/dist/cjs/client/file-fetcher/index.js +72 -191
  5. package/dist/cjs/client/media-client.js +2 -26
  6. package/dist/cjs/client/media-store/error.js +1 -17
  7. package/dist/cjs/client/media-store/index.js +13 -103
  8. package/dist/cjs/client/media-store/resolveAuth.js +2 -23
  9. package/dist/cjs/client/mobile-upload.js +9 -22
  10. package/dist/cjs/client/stargate-client.js +0 -12
  11. package/dist/cjs/constants.js +1 -4
  12. package/dist/cjs/file-streams-cache.js +0 -11
  13. package/dist/cjs/globalMediaEventEmitter.js +0 -3
  14. package/dist/cjs/identifier.js +0 -8
  15. package/dist/cjs/index.js +0 -38
  16. package/dist/cjs/models/artifacts.js +0 -4
  17. package/dist/cjs/models/auth-headers.js +0 -2
  18. package/dist/cjs/models/auth-query-parameters.js +0 -2
  19. package/dist/cjs/models/errors/helpers.js +0 -2
  20. package/dist/cjs/models/errors/index.js +4 -20
  21. package/dist/cjs/models/file-state.js +10 -36
  22. package/dist/cjs/models/media.js +2 -8
  23. package/dist/cjs/upload-controller.js +0 -6
  24. package/dist/cjs/uploader/calculateChunkSize.js +1 -10
  25. package/dist/cjs/uploader/error.js +3 -19
  26. package/dist/cjs/uploader/index.js +3 -43
  27. package/dist/cjs/utils/checkWebpSupport.js +3 -7
  28. package/dist/cjs/utils/convertBase64ToBlob.js +0 -5
  29. package/dist/cjs/utils/createFileDataLoader.js +4 -28
  30. package/dist/cjs/utils/createMediaSubject.js +0 -4
  31. package/dist/cjs/utils/detectEmptyFile.js +8 -15
  32. package/dist/cjs/utils/getDimensionsFromBlob.js +0 -14
  33. package/dist/cjs/utils/getImageDimensionsFromBlob.js +0 -4
  34. package/dist/cjs/utils/getMediaTypeFromUploadableFile.js +0 -3
  35. package/dist/cjs/utils/getVideoDimensionsFromBlob.js +2 -8
  36. package/dist/cjs/utils/hashing/hasherCreator.js +0 -20
  37. package/dist/cjs/utils/hashing/simpleHasher.js +0 -12
  38. package/dist/cjs/utils/hashing/workerHasher.js +3 -24
  39. package/dist/cjs/utils/imageResizeModeToFileImageMode.js +0 -2
  40. package/dist/cjs/utils/isImageRemote.js +0 -5
  41. package/dist/cjs/utils/mediaSubscribable/fromObservable.js +2 -4
  42. package/dist/cjs/utils/mediaSubscribable/index.js +0 -2
  43. package/dist/cjs/utils/mediaSubscribable/toPromise.js +1 -3
  44. package/dist/cjs/utils/mobileUpload/error.js +3 -19
  45. package/dist/cjs/utils/mobileUpload/helpers.js +2 -26
  46. package/dist/cjs/utils/mobileUpload/index.js +0 -4
  47. package/dist/cjs/utils/mobileUpload/servicesCache.js +0 -2
  48. package/dist/cjs/utils/mobileUpload/stateMachine/index.js +0 -22
  49. package/dist/cjs/utils/mobileUpload/stateMachine/states/processing.js +0 -2
  50. package/dist/cjs/utils/mobileUpload/stateMachine/states/uploading.js +0 -7
  51. package/dist/cjs/utils/overrideMediaTypeIfUnknown.js +0 -4
  52. package/dist/cjs/utils/polling/errors.js +1 -17
  53. package/dist/cjs/utils/polling/index.js +1 -30
  54. package/dist/cjs/utils/request/errors.js +8 -24
  55. package/dist/cjs/utils/request/helpers.js +19 -103
  56. package/dist/cjs/utils/request/index.js +15 -26
  57. package/dist/cjs/utils/safeUnsubscribe.js +0 -2
  58. package/dist/cjs/utils/setTimeoutPromise.js +0 -4
  59. package/dist/cjs/utils/shouldFetchRemoteFileStates.js +0 -21
  60. package/dist/cjs/utils/url.js +4 -27
  61. package/dist/cjs/utils/with-media-client-hoc.js +12 -28
  62. package/dist/cjs/version.json +1 -1
  63. package/dist/es2019/client/collection-fetcher.js +6 -18
  64. package/dist/es2019/client/file-fetcher/error.js +0 -2
  65. package/dist/es2019/client/file-fetcher/index.js +49 -91
  66. package/dist/es2019/client/media-client.js +3 -13
  67. package/dist/es2019/client/media-store/error.js +0 -2
  68. package/dist/es2019/client/media-store/index.js +37 -52
  69. package/dist/es2019/client/media-store/resolveAuth.js +1 -5
  70. package/dist/es2019/client/mobile-upload.js +0 -8
  71. package/dist/es2019/client/stargate-client.js +0 -3
  72. package/dist/es2019/constants.js +1 -2
  73. package/dist/es2019/file-streams-cache.js +0 -11
  74. package/dist/es2019/globalMediaEventEmitter.js +0 -4
  75. package/dist/es2019/index.js +4 -11
  76. package/dist/es2019/models/artifacts.js +0 -2
  77. package/dist/es2019/models/errors/index.js +5 -4
  78. package/dist/es2019/models/file-state.js +6 -7
  79. package/dist/es2019/models/media.js +2 -3
  80. package/dist/es2019/upload-controller.js +0 -3
  81. package/dist/es2019/uploader/calculateChunkSize.js +1 -4
  82. package/dist/es2019/uploader/error.js +0 -2
  83. package/dist/es2019/uploader/index.js +0 -13
  84. package/dist/es2019/utils/checkWebpSupport.js +3 -4
  85. package/dist/es2019/utils/convertBase64ToBlob.js +0 -2
  86. package/dist/es2019/utils/createFileDataLoader.js +6 -9
  87. package/dist/es2019/utils/createMediaSubject.js +0 -2
  88. package/dist/es2019/utils/detectEmptyFile.js +1 -5
  89. package/dist/es2019/utils/getDimensionsFromBlob.js +0 -3
  90. package/dist/es2019/utils/getImageDimensionsFromBlob.js +0 -2
  91. package/dist/es2019/utils/getVideoDimensionsFromBlob.js +2 -1
  92. package/dist/es2019/utils/hashing/hasherCreator.js +0 -2
  93. package/dist/es2019/utils/hashing/simpleHasher.js +0 -3
  94. package/dist/es2019/utils/hashing/workerHasher.js +1 -16
  95. package/dist/es2019/utils/isImageRemote.js +0 -2
  96. package/dist/es2019/utils/mediaSubscribable/fromObservable.js +2 -1
  97. package/dist/es2019/utils/mediaSubscribable/toPromise.js +1 -1
  98. package/dist/es2019/utils/mobileUpload/error.js +0 -2
  99. package/dist/es2019/utils/mobileUpload/helpers.js +2 -7
  100. package/dist/es2019/utils/mobileUpload/stateMachine/index.js +0 -2
  101. package/dist/es2019/utils/mobileUpload/stateMachine/states/uploading.js +2 -1
  102. package/dist/es2019/utils/overrideMediaTypeIfUnknown.js +0 -1
  103. package/dist/es2019/utils/polling/errors.js +0 -2
  104. package/dist/es2019/utils/polling/index.js +3 -20
  105. package/dist/es2019/utils/request/errors.js +0 -2
  106. package/dist/es2019/utils/request/helpers.js +24 -35
  107. package/dist/es2019/utils/request/index.js +2 -2
  108. package/dist/es2019/utils/shouldFetchRemoteFileStates.js +1 -5
  109. package/dist/es2019/utils/url.js +6 -14
  110. package/dist/es2019/utils/with-media-client-hoc.js +10 -7
  111. package/dist/es2019/version.json +1 -1
  112. package/dist/esm/client/collection-fetcher.js +4 -34
  113. package/dist/esm/client/file-fetcher/error.js +3 -12
  114. package/dist/esm/client/file-fetcher/index.js +74 -161
  115. package/dist/esm/client/media-client.js +3 -16
  116. package/dist/esm/client/media-store/error.js +1 -10
  117. package/dist/esm/client/media-store/index.js +13 -89
  118. package/dist/esm/client/media-store/resolveAuth.js +2 -13
  119. package/dist/esm/client/mobile-upload.js +9 -15
  120. package/dist/esm/client/stargate-client.js +0 -7
  121. package/dist/esm/constants.js +1 -2
  122. package/dist/esm/file-streams-cache.js +0 -6
  123. package/dist/esm/globalMediaEventEmitter.js +0 -1
  124. package/dist/esm/index.js +4 -11
  125. package/dist/esm/models/artifacts.js +0 -2
  126. package/dist/esm/models/errors/index.js +5 -12
  127. package/dist/esm/models/file-state.js +10 -17
  128. package/dist/esm/models/media.js +2 -3
  129. package/dist/esm/upload-controller.js +0 -2
  130. package/dist/esm/uploader/calculateChunkSize.js +1 -4
  131. package/dist/esm/uploader/error.js +3 -12
  132. package/dist/esm/uploader/index.js +3 -32
  133. package/dist/esm/utils/checkWebpSupport.js +3 -4
  134. package/dist/esm/utils/convertBase64ToBlob.js +0 -3
  135. package/dist/esm/utils/createFileDataLoader.js +4 -18
  136. package/dist/esm/utils/createMediaSubject.js +0 -2
  137. package/dist/esm/utils/detectEmptyFile.js +8 -12
  138. package/dist/esm/utils/getDimensionsFromBlob.js +0 -7
  139. package/dist/esm/utils/getImageDimensionsFromBlob.js +0 -2
  140. package/dist/esm/utils/getVideoDimensionsFromBlob.js +2 -3
  141. package/dist/esm/utils/hashing/hasherCreator.js +0 -9
  142. package/dist/esm/utils/hashing/simpleHasher.js +0 -4
  143. package/dist/esm/utils/hashing/workerHasher.js +3 -18
  144. package/dist/esm/utils/isImageRemote.js +0 -3
  145. package/dist/esm/utils/mediaSubscribable/fromObservable.js +2 -1
  146. package/dist/esm/utils/mediaSubscribable/toPromise.js +1 -1
  147. package/dist/esm/utils/mobileUpload/error.js +3 -12
  148. package/dist/esm/utils/mobileUpload/helpers.js +2 -11
  149. package/dist/esm/utils/mobileUpload/stateMachine/index.js +0 -6
  150. package/dist/esm/utils/mobileUpload/stateMachine/states/uploading.js +0 -3
  151. package/dist/esm/utils/overrideMediaTypeIfUnknown.js +0 -1
  152. package/dist/esm/utils/polling/errors.js +1 -10
  153. package/dist/esm/utils/polling/index.js +1 -27
  154. package/dist/esm/utils/request/errors.js +8 -17
  155. package/dist/esm/utils/request/helpers.js +22 -76
  156. package/dist/esm/utils/request/index.js +15 -20
  157. package/dist/esm/utils/shouldFetchRemoteFileStates.js +1 -15
  158. package/dist/esm/utils/url.js +4 -18
  159. package/dist/esm/utils/with-media-client-hoc.js +12 -17
  160. package/dist/esm/version.json +1 -1
  161. package/package.json +7 -5
@@ -1,14 +1,11 @@
1
1
  export class UploadController {
2
2
  constructor() {}
3
-
4
3
  setAbort(abortFunction) {
5
4
  this.abortFunction = abortFunction;
6
5
  }
7
-
8
6
  abort() {
9
7
  if (this.abortFunction) {
10
8
  this.abortFunction();
11
9
  }
12
10
  }
13
-
14
11
  }
@@ -1,6 +1,7 @@
1
1
  import { DATA_UNIT } from '../models/media';
2
2
  import { MAX_UPLOAD_FILE_SIZE } from '../constants';
3
3
  export const fileSizeError = 'fileSizeExceedsLimit';
4
+
4
5
  /**
5
6
  * This is a helper to dynamically calculate the chunk size for a given file size.
6
7
  *
@@ -15,18 +16,14 @@ export const calculateChunkSize = fileSize => {
15
16
  if (fileSize > MAX_UPLOAD_FILE_SIZE) {
16
17
  throw new Error(fileSizeError);
17
18
  }
18
-
19
19
  if (fileSize <= 5 * DATA_UNIT.GB) {
20
20
  return 5 * DATA_UNIT.MB;
21
21
  }
22
-
23
22
  if (fileSize > 5 * DATA_UNIT.GB && fileSize <= 50 * DATA_UNIT.GB) {
24
23
  return 50 * DATA_UNIT.MB;
25
24
  }
26
-
27
25
  if (fileSize > 50 * DATA_UNIT.GB && fileSize <= 0.95 * DATA_UNIT.TB) {
28
26
  return 100 * DATA_UNIT.MB;
29
27
  }
30
-
31
28
  return 210 * DATA_UNIT.MB;
32
29
  };
@@ -6,7 +6,6 @@ export class UploaderError extends BaseMediaClientError {
6
6
  this.id = id;
7
7
  this.metadata = metadata;
8
8
  }
9
-
10
9
  get attributes() {
11
10
  const {
12
11
  reason,
@@ -23,7 +22,6 @@ export class UploaderError extends BaseMediaClientError {
23
22
  occurrenceKey
24
23
  };
25
24
  }
26
-
27
25
  }
28
26
  export function isUploaderError(err) {
29
27
  return err instanceof UploaderError;
@@ -5,22 +5,18 @@ import { createHasher } from '../utils/hashing/hasherCreator';
5
5
  import { UploaderError } from './error';
6
6
  import { CHUNK_SIZE, PROCESSING_BATCH_SIZE } from '../constants';
7
7
  import { calculateChunkSize, fileSizeError } from './calculateChunkSize';
8
-
9
8
  const hashingFunction = async blob => {
10
9
  const hasher = await createHasher();
11
10
  return hasher.hash(blob);
12
11
  };
13
-
14
12
  const createProbingFunction = (store, deferredUploadId, collectionName, traceContext) => async chunks => {
15
13
  const response = await store.probeChunks(hashedChunks(chunks), await deferredUploadId, collectionName, traceContext);
16
14
  const results = response.data.results;
17
15
  return Object.values(results).map(result => result.exists);
18
16
  };
19
-
20
17
  const createUploadingFunction = (store, deferredUploadId, collectionName, traceContext) => async chunk => {
21
18
  return await store.uploadChunk(chunk.hash, chunk.blob, await deferredUploadId, chunk.partNumber, collectionName, traceContext);
22
19
  };
23
-
24
20
  const createProcessingFunction = (store, deferredUploadId, collection, traceContext) => {
25
21
  let offset = 0;
26
22
  return async chunks => {
@@ -31,7 +27,6 @@ const createProcessingFunction = (store, deferredUploadId, collection, traceCont
31
27
  offset += chunks.length;
32
28
  };
33
29
  };
34
-
35
30
  const createFileFromUpload = async (file, store, uploadableFileUpfrontIds, uploadId, traceContext) => {
36
31
  const {
37
32
  collection,
@@ -52,7 +47,6 @@ const createFileFromUpload = async (file, store, uploadableFileUpfrontIds, uploa
52
47
  replaceFileId: id
53
48
  }, traceContext);
54
49
  };
55
-
56
50
  export const uploadFile = (file, store, uploadableFileUpfrontIds, callbacks, traceContext) => {
57
51
  const {
58
52
  content,
@@ -64,7 +58,6 @@ export const uploadFile = (file, store, uploadableFileUpfrontIds, callbacks, tra
64
58
  occurrenceKey
65
59
  } = uploadableFileUpfrontIds;
66
60
  let chunkSize = CHUNK_SIZE;
67
-
68
61
  try {
69
62
  if (content instanceof Blob) {
70
63
  chunkSize = calculateChunkSize(content.size);
@@ -76,14 +69,12 @@ export const uploadFile = (file, store, uploadableFileUpfrontIds, callbacks, tra
76
69
  occurrenceKey: occurrenceKey
77
70
  }));
78
71
  }
79
-
80
72
  return {
81
73
  cancel: () => {
82
74
  callbacks === null || callbacks === void 0 ? void 0 : callbacks.onUploadFinish('canceled');
83
75
  }
84
76
  };
85
77
  }
86
-
87
78
  const chunkinatorObservable = chunkinator(content, {
88
79
  hashingFunction,
89
80
  hashingConcurrency: 5,
@@ -100,11 +91,8 @@ export const uploadFile = (file, store, uploadableFileUpfrontIds, callbacks, tra
100
91
  callbacks.onProgress(progress);
101
92
  }
102
93
  }
103
-
104
94
  });
105
-
106
95
  const onUploadFinish = callbacks && callbacks.onUploadFinish || (() => {});
107
-
108
96
  const subscription = from(deferredUploadId).pipe(concatMap(uploadId => chunkinatorObservable.pipe(concatMap(() => from(createFileFromUpload(file, store, uploadableFileUpfrontIds, uploadId, traceContext)))))).subscribe({
109
97
  error: err => onUploadFinish(err),
110
98
  complete: () => onUploadFinish()
@@ -116,5 +104,4 @@ export const uploadFile = (file, store, uploadableFileUpfrontIds, callbacks, tra
116
104
  }
117
105
  };
118
106
  };
119
-
120
107
  const hashedChunks = chunks => chunks.map(chunk => chunk.hash);
@@ -8,14 +8,13 @@ export const checkWebpSupport = () => {
8
8
  if (isSupported !== undefined) {
9
9
  return Promise.resolve(isSupported);
10
10
  }
11
-
12
11
  return new Promise(resolve => {
13
- const img = new Image(); // Following base64 encoded binary content is in webp format. If browser supports this standard,
12
+ const img = new Image();
13
+
14
+ // Following base64 encoded binary content is in webp format. If browser supports this standard,
14
15
  // 2px height image will be displayed. If not, standard "not found" image placeholder will be
15
16
  // displayed and it will be not 2px height.
16
-
17
17
  img.src = 'data:image/webp;base64,UklGRi4AAABXRUJQVlA4TCEAAAAvAUAAEB8wAiMwAgSSNtse/cXjxyCCmrYNWPwmHRH9jwMA';
18
-
19
18
  img.onload = img.onerror = () => {
20
19
  isSupported = img.height === 2;
21
20
  resolve(isSupported);
@@ -3,14 +3,12 @@ export const convertBase64ToBlob = base64 => {
3
3
  const base64Data = base64.split(',')[1];
4
4
  const byteCharacters = atob(base64Data);
5
5
  const byteArrays = [];
6
-
7
6
  for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
8
7
  const slice = byteCharacters.slice(offset, offset + sliceSize);
9
8
  const byteNumbers = slice.split('').map((_, i) => slice.charCodeAt(i));
10
9
  const byteArray = new Uint8Array(byteNumbers);
11
10
  byteArrays.push(byteArray);
12
11
  }
13
-
14
12
  return new Blob(byteArrays, {
15
13
  type: 'image/jpeg'
16
14
  });
@@ -4,13 +4,10 @@ import 'setimmediate';
4
4
  import Dataloader from 'dataloader';
5
5
  import { getRandomHex } from '@atlaskit/media-common';
6
6
  export const MAX_BATCH_SIZE = 100;
7
-
8
7
  const isBatchLoadingErrorResult = result => {
9
8
  return result.error instanceof Error;
10
9
  };
11
-
12
10
  const makeCacheKey = (id, collection) => collection ? `${id}-${collection}` : id;
13
-
14
11
  export const getItemsFromKeys = (dataloaderKeys, fileItems) => {
15
12
  const itemsByKey = fileItems.reduce((prev, fileItem) => {
16
13
  const {
@@ -18,7 +15,8 @@ export const getItemsFromKeys = (dataloaderKeys, fileItems) => {
18
15
  collection
19
16
  } = fileItem;
20
17
  const key = makeCacheKey(id, collection);
21
- prev[key] = isBatchLoadingErrorResult(fileItem) ? fileItem.error : { ...fileItem.details,
18
+ prev[key] = isBatchLoadingErrorResult(fileItem) ? fileItem.error : {
19
+ ...fileItem.details,
22
20
  metadataTraceContext: fileItem.metadataTraceContext
23
21
  };
24
22
  return prev;
@@ -32,7 +30,6 @@ export const getItemsFromKeys = (dataloaderKeys, fileItems) => {
32
30
  return itemsByKey[key] || null;
33
31
  });
34
32
  };
35
-
36
33
  /**
37
34
  * Returns a function that, given Array<DataloaderKey>, resolves to an array of same length containing either DataloaderResult or Error.
38
35
  * Such contract is formalised by Dataloader 1.0, @see https://github.com/graphql/dataloader
@@ -47,12 +44,12 @@ export function createBatchLoadingFunc(mediaStore) {
47
44
  const nonCollectionName = '__media-single-file-collection__';
48
45
  const fileIdsByCollection = keys.reduce((acc, key) => {
49
46
  const collectionName = key.collectionName || nonCollectionName;
50
- const fileIds = acc[collectionName] || []; // de-duplicate ids in collection
47
+ const fileIds = acc[collectionName] || [];
51
48
 
49
+ // de-duplicate ids in collection
52
50
  if (fileIds.indexOf(key.id) === -1) {
53
51
  fileIds.push(key.id);
54
52
  }
55
-
56
53
  acc[collectionName] = fileIds;
57
54
  return acc;
58
55
  }, {});
@@ -64,10 +61,10 @@ export function createBatchLoadingFunc(mediaStore) {
64
61
  };
65
62
  const fileIds = fileIdsByCollection[collectionNameKey];
66
63
  const collectionName = collectionNameKey === nonCollectionName ? undefined : collectionNameKey;
67
-
68
64
  try {
69
65
  const response = await mediaStore.getItems(fileIds, collectionName, metadataTraceContext);
70
- const itemsWithMetadataTraceContext = response.data.items.map(item => ({ ...item,
66
+ const itemsWithMetadataTraceContext = response.data.items.map(item => ({
67
+ ...item,
71
68
  metadataTraceContext
72
69
  }));
73
70
  items.push(...itemsWithMetadataTraceContext);
@@ -1,12 +1,10 @@
1
1
  import { ReplaySubject } from 'rxjs/ReplaySubject';
2
2
  export function createMediaSubject(initialState) {
3
3
  const subject = new ReplaySubject(1);
4
-
5
4
  if (initialState instanceof Error) {
6
5
  subject.error(initialState);
7
6
  } else if (initialState) {
8
7
  subject.next(initialState);
9
8
  }
10
-
11
9
  return subject;
12
10
  }
@@ -11,8 +11,7 @@
11
11
  *
12
12
  * Being encapsulated in this function, we can always improve the detection transparently.
13
13
  */
14
- export const EMPTY_FILE_HOURS_ELAPSED_TOLERANCE_MS = 12 * 1000 * 60 * 60;
15
- /* 12 hours */
14
+ export const EMPTY_FILE_HOURS_ELAPSED_TOLERANCE_MS = 12 * 1000 * 60 * 60; /* 12 hours */
16
15
 
17
16
  export function isEmptyFile(fileDetails, now = Date.now()) {
18
17
  const {
@@ -25,14 +24,11 @@ export function isEmptyFile(fileDetails, now = Date.now()) {
25
24
  size,
26
25
  createdAt
27
26
  } = fileDetails;
28
-
29
27
  if (!artifacts && !mediaType && !mimeType && !name && !processingStatus && !representations && !size && typeof createdAt === 'number') {
30
28
  const msSinceFileCreation = now - createdAt;
31
-
32
29
  if (msSinceFileCreation > EMPTY_FILE_HOURS_ELAPSED_TOLERANCE_MS) {
33
30
  return true;
34
31
  }
35
32
  }
36
-
37
33
  return false;
38
34
  }
@@ -5,17 +5,14 @@ export const getDimensionsFromBlob = async (mediaType, blob) => {
5
5
  case 'image':
6
6
  {
7
7
  const url = URL.createObjectURL(blob);
8
-
9
8
  try {
10
9
  return await getImageDimensionsFromBlob(url);
11
10
  } finally {
12
11
  URL.revokeObjectURL(url);
13
12
  }
14
13
  }
15
-
16
14
  case 'video':
17
15
  return getVideoDimensionsFromBlob(blob);
18
-
19
16
  default:
20
17
  throw new Error(`Can't extract dimensions from ${mediaType}`);
21
18
  }
@@ -1,11 +1,9 @@
1
1
  export const getImageDimensionsFromBlob = url => new Promise((resolve, reject) => {
2
2
  const img = new Image();
3
3
  img.src = url;
4
-
5
4
  img.onload = () => resolve({
6
5
  width: img.width,
7
6
  height: img.height
8
7
  });
9
-
10
8
  img.onerror = reject;
11
9
  });
@@ -4,8 +4,9 @@ export const getVideoDimensionsFromBlob = async blob => {
4
4
  const video = document.createElement('video');
5
5
  video.preload = 'metadata';
6
6
  video.src = url;
7
- video.muted = true; // loadedmetadata, loadeddata, play, playing
7
+ video.muted = true;
8
8
 
9
+ // loadedmetadata, loadeddata, play, playing
9
10
  video.addEventListener('loadedmetadata', function timeupdateHandler() {
10
11
  video.removeEventListener('loadedmetadata', timeupdateHandler);
11
12
  resolve({
@@ -2,7 +2,6 @@ let hasher = null;
2
2
  export const destroyHasher = () => hasher = null;
3
3
  export const createHasher = async () => {
4
4
  const numWorkers = 3;
5
-
6
5
  if (!hasher) {
7
6
  try {
8
7
  const {
@@ -16,6 +15,5 @@ export const createHasher = async () => {
16
15
  hasher = new SimpleHasher();
17
16
  }
18
17
  }
19
-
20
18
  return hasher;
21
19
  };
@@ -4,13 +4,10 @@ export class SimpleHasher {
4
4
  return new Promise((resolve, reject) => {
5
5
  const reader = new FileReader();
6
6
  reader.readAsArrayBuffer(blob);
7
-
8
7
  reader.onload = () => {
9
8
  resolve(Rusha.createHash().update(reader.result).digest('hex'));
10
9
  };
11
-
12
10
  reader.onerror = reject;
13
11
  });
14
12
  }
15
-
16
13
  }
@@ -4,18 +4,14 @@ import Rusha from 'rusha';
4
4
  export class WorkerHasher {
5
5
  constructor(numOfWorkers) {
6
6
  _defineProperty(this, "workers", []);
7
-
8
7
  _defineProperty(this, "jobs", {});
9
-
10
8
  for (let i = 0; i < numOfWorkers; ++i) {
11
9
  this.workers.push(this.createWorker());
12
10
  }
13
11
  }
14
-
15
12
  hash(chunk) {
16
13
  return this.calculateHashInWorker(chunk);
17
14
  }
18
-
19
15
  createWorker() {
20
16
  const worker = Rusha.createWorker();
21
17
  const hasherWorker = {
@@ -27,10 +23,8 @@ export class WorkerHasher {
27
23
  });
28
24
  return hasherWorker;
29
25
  }
30
-
31
26
  handleWorkerMessage(event, hasherWorker) {
32
27
  const id = event.data.id;
33
-
34
28
  if (this.jobs[id]) {
35
29
  const {
36
30
  resolve,
@@ -38,7 +32,6 @@ export class WorkerHasher {
38
32
  } = this.jobs[id];
39
33
  delete this.jobs[id];
40
34
  hasherWorker.activeJobs--;
41
-
42
35
  if (event.data.error) {
43
36
  // TODO previously we were just calling it again.
44
37
  // this.calculateHashInWorker(chunk);
@@ -48,7 +41,6 @@ export class WorkerHasher {
48
41
  }
49
42
  }
50
43
  }
51
-
52
44
  calculateHashInWorker(blob) {
53
45
  const jobId = uuidV4();
54
46
  return new Promise((resolve, reject) => {
@@ -60,45 +52,38 @@ export class WorkerHasher {
60
52
  this.dispatch(jobId, worker, blob);
61
53
  });
62
54
  }
63
-
64
55
  dispatch(jobId, hasherWorker, chunkBlob) {
65
56
  hasherWorker.activeJobs++;
66
57
  const worker = hasherWorker.worker;
58
+
67
59
  /*
68
60
  * postMessage() with chunk blob in Safari results in the error
69
61
  * "Failed to load resource: The operation could not be completed. (WebKitBlobResource error 1.)"
70
62
  *
71
63
  * To prevent it, we read the data from the blob using FileReader and pass it via postMessage to the worker.
72
64
  */
73
-
74
65
  if (navigator.userAgent.indexOf('Safari') > -1 && navigator.userAgent.indexOf('Chrome') === -1) {
75
66
  const rd = new FileReader();
76
-
77
67
  rd.onload = () => {
78
68
  worker.postMessage({
79
69
  id: jobId,
80
70
  data: rd.result
81
71
  });
82
72
  };
83
-
84
73
  rd.readAsBinaryString(chunkBlob);
85
74
  return;
86
75
  }
87
-
88
76
  worker.postMessage({
89
77
  id: jobId,
90
78
  data: chunkBlob
91
79
  });
92
80
  }
93
-
94
81
  getMostRelaxedWorker() {
95
82
  return this.workers.reduce((current, next) => {
96
83
  if (next.activeJobs < current.activeJobs) {
97
84
  return next;
98
85
  }
99
-
100
86
  return current;
101
87
  }, this.workers[0]);
102
88
  }
103
-
104
89
  }
@@ -2,12 +2,10 @@
2
2
  export const isImageRemote = (imageUrl, windowOrigin = window.location.origin) => {
3
3
  if (URL && URL.prototype) {
4
4
  const url = new URL(imageUrl);
5
-
6
5
  if (!url.host) {
7
6
  // This is a local resource. Safari will fail to load it if we set crossorigin
8
7
  return false;
9
8
  }
10
-
11
9
  return url.origin !== windowOrigin;
12
10
  } else {
13
11
  // IE doesn't have support to 'new URL'.
@@ -2,7 +2,8 @@ import { createMediaSubject } from '../createMediaSubject';
2
2
  export function fromObservable(observable) {
3
3
  return {
4
4
  subscribe: observer => {
5
- const subscription = // This is needed to handle "subscribe" function overload.
5
+ const subscription =
6
+ // This is needed to handle "subscribe" function overload.
6
7
  // It allows accepting a single "next" callback function as an argument.
7
8
  observer instanceof Function ? observable.subscribe(observer) : observable.subscribe(observer);
8
9
  return {
@@ -1,11 +1,11 @@
1
1
  import { Subscription } from 'rxjs/Subscription';
2
-
3
2
  /**
4
3
  * This is a helper to transform the first value emitted by an MediaSubscribable into a Promise.
5
4
  *
6
5
  * @param mediaSubscribable a given MediaSubscribable<MediaSubscribableItem>
7
6
  * @param subscription a default Subscription (this parameter exists for testing purpose)
8
7
  */
8
+
9
9
  export const toPromise = (mediaSubscribable, subscription = new Subscription()) => {
10
10
  return new Promise((resolve, reject) => subscription.add(mediaSubscribable.subscribe({
11
11
  next: state => {
@@ -6,7 +6,6 @@ export class MobileUploadError extends BaseMediaClientError {
6
6
  this.id = id;
7
7
  this.metadata = metadata;
8
8
  }
9
-
10
9
  get attributes() {
11
10
  const {
12
11
  reason,
@@ -23,7 +22,6 @@ export class MobileUploadError extends BaseMediaClientError {
23
22
  occurrenceKey
24
23
  };
25
24
  }
26
-
27
25
  }
28
26
  export function isMobileUploadError(err) {
29
27
  return err instanceof MobileUploadError;
@@ -13,39 +13,34 @@ export const createMobileFileStateSubject = service => {
13
13
  };
14
14
  export const createMobileDownloadFileStream = (dataloader, id, collectionName, occurrenceKey) => {
15
15
  const subject = createMediaSubject();
16
- const poll = new PollingFunction(); // ensure subject errors if polling exceeds max iterations or uncaught exception in executor
16
+ const poll = new PollingFunction();
17
17
 
18
+ // ensure subject errors if polling exceeds max iterations or uncaught exception in executor
18
19
  poll.onError = error => subject.error(error);
19
-
20
20
  poll.execute(async () => {
21
21
  const response = await dataloader.load({
22
22
  id,
23
23
  collectionName
24
24
  });
25
-
26
25
  if (!response) {
27
26
  throw new MobileUploadError('emptyItems', id, {
28
27
  collectionName,
29
28
  occurrenceKey
30
29
  });
31
30
  }
32
-
33
31
  if (isEmptyFile(response)) {
34
32
  throw new MobileUploadError('zeroVersionFile', id, {
35
33
  collectionName,
36
34
  occurrenceKey
37
35
  });
38
36
  }
39
-
40
37
  const fileState = mapMediaItemToFileState(id, response);
41
38
  subject.next(fileState);
42
-
43
39
  switch (fileState.status) {
44
40
  case 'processing':
45
41
  // the only case for continuing polling, otherwise this function is run once only
46
42
  poll.next();
47
43
  break;
48
-
49
44
  case 'processed':
50
45
  subject.complete();
51
46
  break;
@@ -29,7 +29,6 @@ export const createMobileUploadStateMachine = (dataloader, initialState, collect
29
29
  const {
30
30
  currentFileState
31
31
  } = ctx;
32
-
33
32
  if (isProcessingFileState(currentFileState)) {
34
33
  const {
35
34
  mediaType,
@@ -38,7 +37,6 @@ export const createMobileUploadStateMachine = (dataloader, initialState, collect
38
37
  } = currentFileState;
39
38
  return shouldFetchRemoteFileStates(mediaType, mimeType, preview);
40
39
  }
41
-
42
40
  return false;
43
41
  },
44
42
  fetchRemoteFileStates: ctx => createMobileDownloadFileStream(dataloader, ctx.currentFileState.id, collectionName, ctx.currentFileState.occurrenceKey).pipe(map(fileState => ({
@@ -7,7 +7,8 @@ export const machineUploadingState = {
7
7
  target: 'uploading',
8
8
  cond: (ctx, event) => isUploadingFileState(ctx.currentFileState) && event.progress > ctx.currentFileState.progress,
9
9
  actions: assign({
10
- currentFileState: (ctx, event) => ({ ...ctx.currentFileState,
10
+ currentFileState: (ctx, event) => ({
11
+ ...ctx.currentFileState,
11
12
  progress: event.progress
12
13
  })
13
14
  })
@@ -5,6 +5,5 @@ export const overrideMediaTypeIfUnknown = (fileState, mediaType) => {
5
5
  mediaType
6
6
  };
7
7
  }
8
-
9
8
  return {};
10
9
  };
@@ -5,7 +5,6 @@ export class PollingError extends BaseMediaClientError {
5
5
  this.reason = reason;
6
6
  this.attempts = attempts;
7
7
  }
8
-
9
8
  get attributes() {
10
9
  const {
11
10
  reason,
@@ -16,7 +15,6 @@ export class PollingError extends BaseMediaClientError {
16
15
  attempts
17
16
  };
18
17
  }
19
-
20
18
  }
21
19
  export function isPollingError(err) {
22
20
  return err instanceof PollingError;
@@ -7,6 +7,7 @@ export const defaultPollingOptions = {
7
7
  poll_backoffFactor: 1.25,
8
8
  poll_maxIntervalMs: 200000
9
9
  };
10
+
10
11
  /**
11
12
  * This class encapsulates polling functionality with the following features:
12
13
  *
@@ -18,47 +19,37 @@ export const defaultPollingOptions = {
18
19
  *
19
20
  * IMPORTANT! the executor function must explicitly call ".next()" for the next iteration to run
20
21
  */
21
-
22
22
  export class PollingFunction {
23
23
  constructor(options) {
24
24
  _defineProperty(this, "poll_intervalMs", 0);
25
-
26
25
  _defineProperty(this, "attempt", 1);
27
-
28
26
  _defineProperty(this, "shouldIterate", true);
29
-
30
27
  _defineProperty(this, "timeoutId", 0);
31
-
32
- this.options = { ...defaultPollingOptions,
28
+ this.options = {
29
+ ...defaultPollingOptions,
33
30
  ...options
34
31
  };
35
32
  this.poll_intervalMs = this.options.poll_intervalMs;
36
33
  }
37
-
38
34
  async execute(executor) {
39
35
  const {
40
36
  poll_maxAttempts
41
37
  } = this.options;
42
-
43
38
  if (poll_maxAttempts === 0) {
44
39
  // hard kill, polling disabled
45
40
  return this.fail(new PollingError('pollingMaxAttemptsExceeded', this.attempt));
46
41
  }
47
-
48
42
  try {
49
43
  // executor must explicitly call this.next() for triggering next iteration (pull)
50
44
  this.shouldIterate = false;
51
45
  await executor();
52
-
53
46
  if (!this.shouldIterate) {
54
47
  return;
55
48
  }
56
-
57
49
  if (this.attempt >= poll_maxAttempts) {
58
50
  // max iterations exceeded, let the consumer know
59
51
  return this.fail(new PollingError('pollingMaxAttemptsExceeded', this.attempt));
60
52
  }
61
-
62
53
  this.poll_intervalMs = this.getIntervalMsForIteration(this.attempt);
63
54
  this.attempt++;
64
55
  this.timeoutId = window.setTimeout(() => this.execute(executor), this.poll_intervalMs);
@@ -66,7 +57,6 @@ export class PollingFunction {
66
57
  this.fail(error);
67
58
  }
68
59
  }
69
-
70
60
  fail(error) {
71
61
  const {
72
62
  onError
@@ -74,28 +64,21 @@ export class PollingFunction {
74
64
  this.cancel();
75
65
  onError && onError(error);
76
66
  }
77
-
78
67
  getIntervalMsForIteration(iteration) {
79
68
  let poll_intervalMs = this.options.poll_intervalMs;
80
-
81
69
  if (iteration === 1) {
82
70
  return poll_intervalMs;
83
71
  }
84
-
85
72
  for (let i = 2; i <= iteration; i++) {
86
73
  poll_intervalMs = poll_intervalMs * this.options.poll_backoffFactor;
87
74
  }
88
-
89
75
  return Math.min(Math.round(poll_intervalMs), this.options.poll_maxIntervalMs);
90
76
  }
91
-
92
77
  next() {
93
78
  this.shouldIterate = true;
94
79
  }
95
-
96
80
  cancel() {
97
81
  window.clearTimeout(this.timeoutId);
98
82
  this.timeoutId = 0;
99
83
  }
100
-
101
84
  }