-
Notifications
You must be signed in to change notification settings - Fork 116
Implement Uppy uploader metadata editing and event handling #2288
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
53f0ab8
21c80bf
0cdcc20
2d78c91
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,6 +21,15 @@ import { UploadState } from "../../state/reducers/files"; | |
| import { i18next } from "@translations/invenio_rdm_records/i18next"; | ||
| import { getFilesList, FilesListTable, FileUploaderToolbar } from "../FileUploader"; | ||
| import { useUppyLocale } from "./locale"; | ||
| import { | ||
| defaultAllowedMetaFields, | ||
| getMetaFieldsRenderers, | ||
| onBeforeUploadProcessMetaFields, | ||
| } from "./metaFields"; | ||
| import { | ||
| UPPY_EVENTS, | ||
| getUppyDashboardEventsProps, | ||
| } from "./events"; | ||
|
|
||
| const defaultDashboardProps = { | ||
| showRemoveButtonAfterComplete: false, | ||
|
|
@@ -79,6 +88,7 @@ export const UppyUploaderComponent = ({ | |
| permissions, | ||
| record, | ||
| initializeFileUpload, | ||
| updateFileMetadata, | ||
| finalizeUpload, | ||
| deleteFile, | ||
| uploadPart, | ||
|
|
@@ -92,13 +102,16 @@ export const UppyUploaderComponent = ({ | |
| decimalSizeDisplay, | ||
| filesLocked, | ||
| allowEmptyFiles, | ||
| allowedMetaFields, | ||
| startEvent, | ||
| ...uiProps | ||
| }) => { | ||
| // We extract the working copy of the draft stored as `values` in formik | ||
| const { values: formikDraft, errors, initialErrors } = useFormikContext(); | ||
| const { filesList } = getFilesList(files ?? {}); | ||
| const hasError = (errors.files || initialErrors?.files) && files; | ||
| const locale = useUppyLocale(); | ||
| const activeAllowedMetaFields = config?.allowedMetaFields || allowedMetaFields || defaultAllowedMetaFields; | ||
| const filesEnabled = _get(formikDraft, "files.enabled", false); | ||
| const filesSize = filesList.reduce((totalSize, file) => (totalSize += file.size), 0); | ||
| const lockFileUploader = !isDraftRecord && filesLocked; | ||
|
|
@@ -147,6 +160,7 @@ export const UppyUploaderComponent = ({ | |
| quota, | ||
| // Bind Redux file actions to the uploader plugin | ||
| initializeFileUpload, | ||
| updateFileMetadata, | ||
| finalizeUpload, | ||
| saveAndFetchDraft, | ||
| setUploadProgress, | ||
|
|
@@ -169,6 +183,16 @@ export const UppyUploaderComponent = ({ | |
| uppy.setOptions({ onBeforeFileAdded }); | ||
| }, [uppy, filesList]); | ||
|
|
||
| React.useEffect(() => { | ||
| uppy.setOptions({ | ||
| onBeforeUpload: (uppyFiles) => { | ||
| return activeAllowedMetaFields && activeAllowedMetaFields.length > 0 | ||
| ? onBeforeUploadProcessMetaFields(uppyFiles, files, activeAllowedMetaFields) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Files from closure not in deps, could get stale |
||
| : uppyFiles; | ||
| } | ||
| }); | ||
| }, [uppy, activeAllowedMetaFields]); | ||
|
|
||
| React.useEffect(() => { | ||
| const uploaderPlugin = uppy.getPlugin("RDMUppyUploaderPlugin"); | ||
| if (uploaderPlugin) { | ||
|
|
@@ -304,10 +328,10 @@ export const UppyUploaderComponent = ({ | |
| disabled={!filesLeft || lockFileUploader} | ||
| // `null` means "do not display a Done button in a status bar" | ||
| doneButtonHandler={null} | ||
| note={i18next.t( | ||
| "File addition, removal or modification are not allowed after you have published your upload." | ||
| )} | ||
| note={i18next.t("File addition, removal or modification are not allowed after you have published your upload.")} | ||
| metaFields={activeAllowedMetaFields && activeAllowedMetaFields.length > 0 ? getMetaFieldsRenderers(activeAllowedMetaFields) : undefined} | ||
| {...defaultDashboardProps} | ||
| {...(startEvent && getUppyDashboardEventsProps(startEvent))} | ||
| {...uiProps} | ||
| /> | ||
| )} | ||
|
|
@@ -379,6 +403,7 @@ UppyUploaderComponent.propTypes = { | |
| isFileImportInProgress: PropTypes.bool, | ||
| importParentFiles: PropTypes.func.isRequired, | ||
| initializeFileUpload: PropTypes.func.isRequired, | ||
| updateFileMetadata: PropTypes.func.isRequired, | ||
| finalizeUpload: PropTypes.func.isRequired, | ||
| uploadPart: PropTypes.func.isRequired, | ||
| setUploadProgress: PropTypes.func.isRequired, | ||
|
|
@@ -388,6 +413,20 @@ UppyUploaderComponent.propTypes = { | |
| filesLocked: PropTypes.bool, | ||
| permissions: PropTypes.object, | ||
| allowEmptyFiles: PropTypes.bool, | ||
| allowedMetaFields: PropTypes.arrayOf( | ||
| PropTypes.shape({ | ||
| id: PropTypes.string.isRequired, | ||
| defaultValue: PropTypes.any, | ||
| name: PropTypes.string, | ||
| placeholder: PropTypes.string, | ||
| render: PropTypes.func, | ||
| condition: PropTypes.func, | ||
| }) | ||
| ), | ||
| startEvent: PropTypes.shape({ | ||
| event: PropTypes.oneOf(Object.values(UPPY_EVENTS)).isRequired, | ||
| payload: PropTypes.object, | ||
| }), | ||
| }; | ||
|
|
||
| UppyUploaderComponent.defaultProps = { | ||
|
|
@@ -408,4 +447,6 @@ UppyUploaderComponent.defaultProps = { | |
| decimalSizeDisplay: true, | ||
| filesLocked: false, | ||
| allowEmptyFiles: true, | ||
| allowedMetaFields: undefined, | ||
| startEvent: undefined, | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| // This file is part of Invenio-RDM-Records | ||
| // Copyright (C) 2020-2025 CERN. | ||
| // Copyright (C) 2025 CESNET. | ||
| // | ||
| // Invenio-RDM-Records is free software; you can redistribute it and/or modify it | ||
| // under the terms of the MIT License; see LICENSE file for more details. | ||
|
|
||
| /** | ||
| * Enumeration of possible supported Uppy dashboard events. | ||
| * @constant {Object} | ||
| * @property {string} UPLOAD_FILE_WITHOUT_EDIT - Event triggered for auto-uploading without explicit editing user interaction. | ||
| */ | ||
| export const UPPY_EVENTS = { | ||
| UPLOAD_FILE_WITHOUT_EDIT: "upload-file-without-edit", | ||
| }; | ||
|
|
||
| /** | ||
| * Computes conditional UI properties for the Uppy Dashboard based on the active event state. | ||
| * | ||
| * @param {Object} startEvent - The currently active event, if any (e.g. { event: UPPY_EVENTS.EDIT_FILE }). | ||
| * @returns {Object} A dictionary of props specifically mapped to the `<Dashboard />` component dynamically. | ||
| */ | ||
| export const getUppyDashboardEventsProps = (startEvent) => { | ||
| return { | ||
| showSelectedFiles: startEvent.event === UPPY_EVENTS.UPLOAD_FILE_WITHOUT_EDIT ? false : true, | ||
| }; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,6 +10,7 @@ import { | |
| deleteFile, | ||
| importParentFiles, | ||
| initializeFileUpload, | ||
| updateFileMetadata, | ||
| uploadPart, | ||
| finalizeUpload, | ||
| saveAndFetchDraft, | ||
|
|
@@ -34,6 +35,7 @@ const mapStateToProps = (state) => { | |
|
|
||
| const mapDispatchToProps = (dispatch) => ({ | ||
| initializeFileUpload: (draft, file) => dispatch(initializeFileUpload(draft, file)), | ||
| updateFileMetadata: (draft, serverFile, file) => dispatch(updateFileMetadata(draft, serverFile, file)), | ||
| finalizeUpload: (file) => dispatch(finalizeUpload(file.meta.links.commit, file)), | ||
| importParentFiles: () => dispatch(importParentFiles()), | ||
| setUploadProgress: (file, percent) => dispatch(setUploadProgress(file, percent)), | ||
|
|
@@ -46,3 +48,6 @@ export const UppyUploader = connect( | |
| mapStateToProps, | ||
| mapDispatchToProps | ||
| )(UppyUploaderComponent); | ||
|
|
||
| export { UPPY_EVENTS } from "./events"; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Only used internally, is there a reason to export for other parts of UI?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To make available events a public enum. Not just a string literal. But it's optional ofc. |
||
| export { defaultAllowedMetaFields } from "./metaFields"; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If metadata update fails, the whole upload will fail without calling finalize, will it be properly cleaned up? I think finalize will handle cleanup properly if it fails