@aws-amplify/ui-react-storage 3.0.18 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/dist/esm/components/StorageImage/StorageImage.mjs +46 -20
  2. package/dist/esm/components/StorageManager/StorageManager.mjs +23 -10
  3. package/dist/esm/components/StorageManager/hooks/useStorageManager/actions.mjs +31 -45
  4. package/dist/esm/components/StorageManager/hooks/useStorageManager/reducer.mjs +1 -1
  5. package/dist/esm/components/StorageManager/hooks/useUploadFiles/useUploadFiles.mjs +34 -31
  6. package/dist/esm/components/StorageManager/ui/FileList/FileDetails.mjs +1 -0
  7. package/dist/esm/components/StorageManager/utils/getInput.mjs +25 -0
  8. package/dist/esm/components/StorageManager/{hooks/useUploadFiles → utils}/resolveFile.mjs +2 -4
  9. package/dist/esm/components/StorageManager/utils/uploadFile.mjs +18 -21
  10. package/dist/esm/version.mjs +1 -1
  11. package/dist/index.js +264 -222
  12. package/dist/types/components/StorageImage/StorageImage.d.ts +6 -2
  13. package/dist/types/components/StorageImage/types.d.ts +30 -0
  14. package/dist/types/components/StorageManager/StorageManager.d.ts +5 -2
  15. package/dist/types/components/StorageManager/hooks/useStorageManager/actions.d.ts +2 -5
  16. package/dist/types/components/StorageManager/hooks/useStorageManager/types.d.ts +2 -2
  17. package/dist/types/components/StorageManager/hooks/useStorageManager/useStorageManager.d.ts +2 -5
  18. package/dist/types/components/StorageManager/hooks/useUploadFiles/useUploadFiles.d.ts +4 -1
  19. package/dist/types/components/StorageManager/types.d.ts +17 -7
  20. package/dist/types/components/StorageManager/ui/FileList/types.d.ts +4 -14
  21. package/dist/types/components/StorageManager/utils/getInput.d.ts +13 -0
  22. package/dist/types/components/StorageManager/utils/index.d.ts +2 -1
  23. package/dist/types/components/StorageManager/utils/resolveFile.d.ts +9 -0
  24. package/dist/types/components/StorageManager/utils/uploadFile.d.ts +31 -12
  25. package/dist/types/version.d.ts +1 -1
  26. package/package.json +6 -6
  27. package/dist/types/components/StorageManager/hooks/useUploadFiles/resolveFile.d.ts +0 -10
@@ -1,31 +1,57 @@
1
1
  import * as React from 'react';
2
- import { isUndefined, classNames, ComponentClassName } from '@aws-amplify/ui';
2
+ import { classNames, ComponentClassName } from '@aws-amplify/ui';
3
3
  import { Image } from '@aws-amplify/ui-react';
4
- import { useStorageURL } from '@aws-amplify/ui-react/internal';
5
- import { useSetUserAgent } from '@aws-amplify/ui-react-core';
4
+ import { useDeprecationWarning } from '@aws-amplify/ui-react/internal';
5
+ import { useSetUserAgent, useGetUrl } from '@aws-amplify/ui-react-core';
6
6
  import { VERSION } from '../../version.mjs';
7
7
 
8
- const StorageImage = ({ accessLevel, className, fallbackSrc, identityId, imgKey, onStorageGetError, validateObjectExistence, ...rest }) => {
9
- const resolvedValidateObjectExistence = isUndefined(validateObjectExistence)
10
- ? true
11
- : validateObjectExistence;
12
- const options = React.useMemo(() => ({
13
- accessLevel,
14
- targetIdentityId: identityId,
15
- validateObjectExistence: resolvedValidateObjectExistence,
16
- }), [accessLevel, identityId, resolvedValidateObjectExistence]);
8
+ const MISSING_REQUIRED_PROP_MESSAGE = '`StorageImage` requires either an `imgKey` or `path` prop.';
9
+ const HAS_DEPRECATED_PROPS_MESSAGE = '`imgKey`, `accessLevel`, and `identityId` will be replaced with `path` in a future major version. See https://ui.docs.amplify.aws/react/connected-components/storage/storageimage#props';
10
+ const HAS_PATH_AND_KEY_MESSAGE = '`imgKey` is ignored when both `imgKey` and `path` props are provided.';
11
+ const HAS_PATH_AND_UNSUPPORTED_OPTIONS_MESSAGE = '`accessLevel` and `identityId` are ignored when the `path` prop is provided.';
12
+ const getDeprecationMessage = ({ hasImgkey, hasPath, hasDeprecatedOptions, }) => {
13
+ let message = '';
14
+ if (hasPath && hasImgkey) {
15
+ message = HAS_PATH_AND_KEY_MESSAGE;
16
+ }
17
+ else if (hasPath && hasDeprecatedOptions) {
18
+ message = HAS_PATH_AND_UNSUPPORTED_OPTIONS_MESSAGE;
19
+ }
20
+ else if (hasImgkey) {
21
+ message = HAS_DEPRECATED_PROPS_MESSAGE;
22
+ }
23
+ return message;
24
+ };
25
+ const StorageImage = ({ accessLevel, className, fallbackSrc, identityId, imgKey, path, onStorageGetError, onGetUrlError, validateObjectExistence = true, ...rest }) => {
26
+ const hasImgkey = !!imgKey;
27
+ const hasPath = !!path;
28
+ const hasDeprecatedOptions = !!accessLevel || !!identityId;
29
+ const message = getDeprecationMessage({
30
+ hasDeprecatedOptions,
31
+ hasImgkey,
32
+ hasPath,
33
+ });
34
+ useDeprecationWarning({ message, shouldWarn: !!message });
35
+ if (!hasImgkey && !hasPath) {
36
+ throw new Error(MISSING_REQUIRED_PROP_MESSAGE);
37
+ }
17
38
  useSetUserAgent({
18
39
  componentName: 'StorageImage',
19
40
  packageName: 'react-storage',
20
41
  version: VERSION,
21
42
  });
22
- const url = useStorageURL({
23
- key: imgKey,
24
- options,
25
- fallbackURL: fallbackSrc,
26
- onStorageGetError,
27
- });
28
- return (React.createElement(Image, { ...rest, className: classNames(ComponentClassName.StorageImage, className), src: url }));
43
+ const onError = onGetUrlError ?? onStorageGetError;
44
+ const input = React.useMemo(() => ({
45
+ ...(path ? { path } : { key: imgKey }),
46
+ onError,
47
+ options: {
48
+ accessLevel,
49
+ targetIdentityId: identityId,
50
+ validateObjectExistence,
51
+ },
52
+ }), [accessLevel, imgKey, identityId, onError, path, validateObjectExistence]);
53
+ const { url } = useGetUrl(input);
54
+ return (React.createElement(Image, { ...rest, className: classNames(ComponentClassName.StorageImage, className), src: url?.toString() ?? fallbackSrc }));
29
55
  };
30
56
 
31
- export { StorageImage };
57
+ export { HAS_DEPRECATED_PROPS_MESSAGE, HAS_PATH_AND_KEY_MESSAGE, HAS_PATH_AND_UNSUPPORTED_OPTIONS_MESSAGE, MISSING_REQUIRED_PROP_MESSAGE, StorageImage };
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import { getLogger, ComponentClassName } from '@aws-amplify/ui';
3
3
  import { VisuallyHidden } from '@aws-amplify/ui-react';
4
- import { useSetUserAgent } from '@aws-amplify/ui-react-core';
4
+ import { useDeprecationWarning, useSetUserAgent } from '@aws-amplify/ui-react-core';
5
5
  import { useDropZone } from '@aws-amplify/ui-react/internal';
6
6
  import { useStorageManager } from './hooks/useStorageManager/useStorageManager.mjs';
7
7
  import { useUploadFiles } from './hooks/useUploadFiles/useUploadFiles.mjs';
@@ -15,14 +15,26 @@ import { FilePicker } from './ui/FilePicker/FilePicker.mjs';
15
15
  import { checkMaxFileSize } from './utils/checkMaxFileSize.mjs';
16
16
  import { defaultStorageManagerDisplayText } from './utils/displayText.mjs';
17
17
  import { filterAllowedFiles } from './utils/filterAllowedFiles.mjs';
18
+ import 'aws-amplify/auth';
18
19
  import 'aws-amplify/storage';
19
20
  import { VERSION } from '../../version.mjs';
20
21
 
21
22
  const logger = getLogger('Storage');
22
- function StorageManagerBase({ acceptedFileTypes = [], accessLevel, autoUpload = true, defaultFiles, displayText: overrideDisplayText, isResumable = false, maxFileCount, maxFileSize, onUploadError, onUploadSuccess, onFileRemove, onUploadStart, showThumbnails = true, processFile, components, path, }, ref) {
23
- if (!accessLevel || !maxFileCount) {
24
- logger.warn('StorageManager requires accessLevel and maxFileCount props');
23
+ const MISSING_REQUIRED_PROPS_MESSAGE = '`StorageManager` requires a `maxFileCount` prop to be provided.';
24
+ const ACCESS_LEVEL_WITH_PATH_CALLBACK_MESSAGE = '`StorageManager` does not allow usage of a `path` callback prop with an `accessLevel` prop.';
25
+ const ACCESS_LEVEL_DEPRECATION_MESSAGE = '`accessLevel` has been deprecated and will be removed in a future major version. See migration notes at https://ui.docs.amplify.aws/react/connected-components/storage/storagemanager';
26
+ const StorageManagerBase = React.forwardRef(function StorageManager({ acceptedFileTypes = [], accessLevel, autoUpload = true, components, defaultFiles, displayText: overrideDisplayText, isResumable = false, maxFileCount, maxFileSize, onFileRemove, onUploadError, onUploadStart, onUploadSuccess, path, processFile, showThumbnails = true, }, ref) {
27
+ if (!maxFileCount) {
28
+ // eslint-disable-next-line no-console
29
+ console.warn(MISSING_REQUIRED_PROPS_MESSAGE);
25
30
  }
31
+ if (accessLevel && typeof path === 'function') {
32
+ throw new Error(ACCESS_LEVEL_WITH_PATH_CALLBACK_MESSAGE);
33
+ }
34
+ useDeprecationWarning({
35
+ message: ACCESS_LEVEL_DEPRECATION_MESSAGE,
36
+ shouldWarn: !!accessLevel,
37
+ });
26
38
  const Components = {
27
39
  Container,
28
40
  DropZone,
@@ -95,15 +107,15 @@ function StorageManagerBase({ acceptedFileTypes = [], accessLevel, autoUpload =
95
107
  const onUploadAll = () => {
96
108
  queueFiles();
97
109
  };
98
- const onPauseUpload = ({ id, uploadTask, }) => {
110
+ const onPauseUpload = ({ id, uploadTask }) => {
99
111
  uploadTask.pause();
100
112
  setUploadPaused({ id });
101
113
  };
102
- const onResumeUpload = ({ id, uploadTask, }) => {
114
+ const onResumeUpload = ({ id, uploadTask }) => {
103
115
  uploadTask.resume();
104
116
  setUploadResumed({ id });
105
117
  };
106
- const onCancelUpload = ({ id, uploadTask, }) => {
118
+ const onCancelUpload = ({ id, uploadTask }) => {
107
119
  // At this time we don't know if the delete
108
120
  // permissions are enabled (required to cancel upload),
109
121
  // so we do a pause instead and remove from files
@@ -155,8 +167,9 @@ function StorageManagerBase({ acceptedFileTypes = [], accessLevel, autoUpload =
155
167
  hasFiles ? (React.createElement(Components.FileListHeader, { allUploadsSuccessful: allUploadsSuccessful, displayText: displayText, fileCount: files.length, remainingFilesCount: remainingFilesCount, selectedFilesCount: selectedFilesCount })) : null,
156
168
  React.createElement(Components.FileList, { displayText: displayText, files: files, isResumable: isResumable, onCancelUpload: onCancelUpload, onDeleteUpload: onDeleteUpload, onResume: onResumeUpload, onPause: onPauseUpload, showThumbnails: showThumbnails, hasMaxFilesError: hasMaxFilesError, maxFileCount: maxFileCount }),
157
169
  hasUploadActions ? (React.createElement(Components.FileListFooter, { displayText: displayText, remainingFilesCount: remainingFilesCount, onClearAll: onClearAll, onUploadAll: onUploadAll })) : null));
158
- }
159
- const StorageManager = Object.assign(React.forwardRef(StorageManagerBase), {
170
+ });
171
+ // pass an empty object as first param to avoid destructive action on `StorageManagerBase`
172
+ const StorageManager = Object.assign({}, StorageManagerBase, {
160
173
  Container,
161
174
  DropZone,
162
175
  FileList,
@@ -165,4 +178,4 @@ const StorageManager = Object.assign(React.forwardRef(StorageManagerBase), {
165
178
  FilePicker,
166
179
  });
167
180
 
168
- export { StorageManager };
181
+ export { ACCESS_LEVEL_DEPRECATION_MESSAGE, ACCESS_LEVEL_WITH_PATH_CALLBACK_MESSAGE, MISSING_REQUIRED_PROPS_MESSAGE, StorageManager };
@@ -1,49 +1,35 @@
1
1
  import { StorageManagerActionTypes } from './types.mjs';
2
2
 
3
- const addFilesAction = ({ files, status, getFileErrorMessage, }) => {
4
- return {
5
- type: StorageManagerActionTypes.ADD_FILES,
6
- files,
7
- status,
8
- getFileErrorMessage,
9
- };
10
- };
11
- const clearFilesAction = () => {
12
- return {
13
- type: StorageManagerActionTypes.CLEAR_FILES,
14
- };
15
- };
16
- const queueFilesAction = () => {
17
- return {
18
- type: StorageManagerActionTypes.QUEUE_FILES,
19
- };
20
- };
21
- const setUploadingFileAction = ({ id, uploadTask, }) => {
22
- return {
23
- type: StorageManagerActionTypes.SET_STATUS_UPLOADING,
24
- id,
25
- uploadTask,
26
- };
27
- };
28
- const setUploadProgressAction = ({ id, progress, }) => {
29
- return {
30
- type: StorageManagerActionTypes.SET_UPLOAD_PROGRESS,
31
- id,
32
- progress,
33
- };
34
- };
35
- const setUploadStatusAction = ({ id, status, }) => {
36
- return {
37
- type: StorageManagerActionTypes.SET_STATUS,
38
- id,
39
- status,
40
- };
41
- };
42
- const removeUploadAction = ({ id }) => {
43
- return {
44
- type: StorageManagerActionTypes.REMOVE_UPLOAD,
45
- id,
46
- };
47
- };
3
+ const addFilesAction = ({ files, status, getFileErrorMessage, }) => ({
4
+ type: StorageManagerActionTypes.ADD_FILES,
5
+ files,
6
+ status,
7
+ getFileErrorMessage,
8
+ });
9
+ const clearFilesAction = () => ({
10
+ type: StorageManagerActionTypes.CLEAR_FILES,
11
+ });
12
+ const queueFilesAction = () => ({
13
+ type: StorageManagerActionTypes.QUEUE_FILES,
14
+ });
15
+ const setUploadingFileAction = ({ id, uploadTask, }) => ({
16
+ type: StorageManagerActionTypes.SET_STATUS_UPLOADING,
17
+ id,
18
+ uploadTask,
19
+ });
20
+ const setUploadProgressAction = ({ id, progress, }) => ({
21
+ type: StorageManagerActionTypes.SET_UPLOAD_PROGRESS,
22
+ id,
23
+ progress,
24
+ });
25
+ const setUploadStatusAction = ({ id, status, }) => ({
26
+ type: StorageManagerActionTypes.SET_STATUS,
27
+ id,
28
+ status,
29
+ });
30
+ const removeUploadAction = ({ id }) => ({
31
+ type: StorageManagerActionTypes.REMOVE_UPLOAD,
32
+ id,
33
+ });
48
34
 
49
35
  export { addFilesAction, clearFilesAction, queueFilesAction, removeUploadAction, setUploadProgressAction, setUploadStatusAction, setUploadingFileAction };
@@ -60,7 +60,7 @@ function storageManagerStateReducer(state, action) {
60
60
  ...currentFile,
61
61
  status: FileStatus.UPLOADING,
62
62
  progress: 0,
63
- uploadTask: uploadTask ? uploadTask : undefined,
63
+ uploadTask,
64
64
  },
65
65
  ];
66
66
  }
@@ -1,8 +1,8 @@
1
1
  import * as React from 'react';
2
- import { isString, isFunction } from '@aws-amplify/ui';
2
+ import { isFunction } from '@aws-amplify/ui';
3
+ import { getInput } from '../../utils/getInput.mjs';
3
4
  import { uploadFile } from '../../utils/uploadFile.mjs';
4
5
  import { FileStatus } from '../../types.mjs';
5
- import { resolveFile } from './resolveFile.mjs';
6
6
 
7
7
  function useUploadFiles({ files, accessLevel, isResumable, setUploadProgress, setUploadingFile, setUploadSuccess, onUploadError, onUploadSuccess, onUploadStart, maxFileCount, processFile, path, }) {
8
8
  React.useEffect(() => {
@@ -11,44 +11,47 @@ function useUploadFiles({ files, accessLevel, isResumable, setUploadProgress, se
11
11
  return;
12
12
  }
13
13
  for (const { file, key, id } of filesReadyToUpload) {
14
- const onComplete = (event) => {
15
- if (isFunction(onUploadSuccess)) {
16
- onUploadSuccess(event);
17
- }
18
- setUploadSuccess({ id });
19
- };
20
14
  const onProgress = (event) => {
21
15
  /**
22
16
  * When a file is zero bytes, the progress.total will equal zero.
23
17
  * Therefore, this will prevent a divide by zero error.
24
18
  */
25
- const progressPercentage = event.totalBytes === undefined || event.totalBytes === 0
19
+ const progress = event.totalBytes === undefined || event.totalBytes === 0
26
20
  ? 100
27
21
  : Math.floor((event.transferredBytes / event.totalBytes) * 100);
28
- setUploadProgress({ id, progress: progressPercentage });
22
+ setUploadProgress({ id, progress });
29
23
  };
30
24
  if (file) {
31
- resolveFile({ processFile, file, key }).then(({ key: processedKey, ...rest }) => {
32
- // prepend `path` to `processedKey`
33
- const resolvedKey = isString(path)
34
- ? `${path}${processedKey}`
35
- : processedKey;
36
- if (isFunction(onUploadStart)) {
37
- onUploadStart({ key: resolvedKey });
38
- }
39
- const uploadTask = uploadFile({
40
- ...rest,
41
- key: resolvedKey,
42
- level: accessLevel,
43
- progressCallback: onProgress,
44
- errorCallback: (error) => {
45
- if (isFunction(onUploadError)) {
46
- onUploadError(error, { key: resolvedKey });
47
- }
48
- },
49
- completeCallback: onComplete,
50
- });
51
- setUploadingFile({ id, uploadTask });
25
+ const input = getInput({
26
+ accessLevel,
27
+ file,
28
+ key,
29
+ onProgress,
30
+ path,
31
+ processFile,
32
+ });
33
+ uploadFile({
34
+ input,
35
+ onComplete: (event) => {
36
+ if (isFunction(onUploadSuccess)) {
37
+ onUploadSuccess({
38
+ key: event.key ??
39
+ event.path,
40
+ });
41
+ }
42
+ setUploadSuccess({ id });
43
+ },
44
+ onError: ({ key, error }) => {
45
+ if (isFunction(onUploadError)) {
46
+ onUploadError(error.message, { key });
47
+ }
48
+ },
49
+ onStart: ({ key, uploadTask }) => {
50
+ if (isFunction(onUploadStart)) {
51
+ onUploadStart({ key });
52
+ }
53
+ setUploadingFile({ id, uploadTask });
54
+ },
52
55
  });
53
56
  }
54
57
  }
@@ -2,6 +2,7 @@ import React__default from 'react';
2
2
  import { ComponentClassName } from '@aws-amplify/ui';
3
3
  import { View, Text } from '@aws-amplify/ui-react';
4
4
  import { humanFileSize } from '../../utils/humanFileSize.mjs';
5
+ import 'aws-amplify/auth';
5
6
  import 'aws-amplify/storage';
6
7
 
7
8
  const UploadDetails = ({ displayName, fileSize, }) => {
@@ -0,0 +1,25 @@
1
+ import { fetchAuthSession } from 'aws-amplify/auth';
2
+ import { isTypedFunction, isString } from '@aws-amplify/ui';
3
+ import { resolveFile } from './resolveFile.mjs';
4
+
5
+ const getInput = ({ accessLevel, file, key, onProgress, path, processFile, }) => {
6
+ return async () => {
7
+ const hasCallbackPath = isTypedFunction(path);
8
+ const hasStringPath = isString(path);
9
+ const hasKeyInput = !!accessLevel && !hasCallbackPath;
10
+ const { file: data, key: fileKey, ...rest } = await resolveFile({ file, key, processFile });
11
+ const contentType = file.type || 'binary/octet-stream';
12
+ // IMPORTANT: always pass `...rest` here for backwards compatibility
13
+ const options = { contentType, onProgress, ...rest };
14
+ if (hasKeyInput) {
15
+ // legacy handling of `path` is to prefix to `fileKey`
16
+ const resolvedKey = hasStringPath ? `${path}${fileKey}` : fileKey;
17
+ return { data, key: resolvedKey, options: { ...options, accessLevel } };
18
+ }
19
+ const { identityId } = await fetchAuthSession();
20
+ const resolvedPath = `${hasCallbackPath ? path({ identityId }) : path}${fileKey}`;
21
+ return { data: file, path: resolvedPath, options };
22
+ };
23
+ };
24
+
25
+ export { getInput };
@@ -5,11 +5,9 @@ import { isFunction } from '@aws-amplify/ui';
5
5
  * and returns a Promise that resolves to { file, key, ..rest }
6
6
  * regardless if processFile is defined and if it is sync or async
7
7
  */
8
- const resolveFile = ({ processFile, file, key, }) => {
8
+ const resolveFile = ({ processFile, ...input }) => {
9
9
  return new Promise((resolve, reject) => {
10
- const result = isFunction(processFile)
11
- ? processFile({ file, key })
12
- : { file, key };
10
+ const result = isFunction(processFile) ? processFile(input) : input;
13
11
  if (result instanceof Promise) {
14
12
  result.then(resolve).catch(reject);
15
13
  }
@@ -1,29 +1,26 @@
1
1
  import { uploadData } from 'aws-amplify/storage';
2
+ import { isFunction } from '@aws-amplify/ui';
2
3
 
3
- function uploadFile({ file, key, level = 'private', progressCallback: onProgress, errorCallback, completeCallback, ...rest }) {
4
- const contentType = file.type || 'binary/octet-stream';
5
- const input = {
6
- key,
7
- data: file,
8
- options: {
9
- accessLevel: level,
10
- contentType,
11
- onProgress,
12
- ...rest,
13
- },
14
- };
15
- const output = uploadData(input);
16
- output.result
17
- .then(() => {
18
- if (output.state === 'SUCCESS') {
19
- completeCallback?.({ key });
4
+ async function uploadFile({ input, onError, onStart, onComplete, }) {
5
+ const resolvedInput = await input();
6
+ const uploadTask = uploadData(resolvedInput);
7
+ const key = resolvedInput?.key ??
8
+ resolvedInput?.path;
9
+ if (isFunction(onStart)) {
10
+ onStart({ key, uploadTask });
11
+ }
12
+ uploadTask.result
13
+ .then((result) => {
14
+ if (isFunction(onComplete) && uploadTask.state === 'SUCCESS') {
15
+ onComplete(result);
20
16
  }
21
17
  })
22
- .catch((e) => {
23
- const error = e;
24
- errorCallback?.(error.message);
18
+ .catch((error) => {
19
+ if (isFunction(onError)) {
20
+ onError({ key, error });
21
+ }
25
22
  });
26
- return output;
23
+ return uploadTask;
27
24
  }
28
25
 
29
26
  export { uploadFile };
@@ -1,3 +1,3 @@
1
- const VERSION = '3.0.18';
1
+ const VERSION = '3.1.0';
2
2
 
3
3
  export { VERSION };