diff --git a/src/main/java/org/sagebionetworks/web/client/FeatureFlagKey.java b/src/main/java/org/sagebionetworks/web/client/FeatureFlagKey.java index 0960d6cddd..04eb963931 100644 --- a/src/main/java/org/sagebionetworks/web/client/FeatureFlagKey.java +++ b/src/main/java/org/sagebionetworks/web/client/FeatureFlagKey.java @@ -40,6 +40,9 @@ public enum FeatureFlagKey { // If enabled, sharing settings will appear in a dialog immediately after uploading one or more files. SHOW_SHARING_SETTINGS_AFTER_UPLOAD("SHOW_SHARING_SETTINGS_AFTER_UPLOAD"), + // If enabled, uses the v2 uploader (react implementation) for file entity uploads. + UPLOADER_V2("UPLOADER_V2"), + // Last flag is used only for tests TEST_FLAG_ONLY("TEST_FLAG_ONLY"); diff --git a/src/main/java/org/sagebionetworks/web/client/PortalGinInjector.java b/src/main/java/org/sagebionetworks/web/client/PortalGinInjector.java index fa0a6e22ed..89b96cb5b9 100644 --- a/src/main/java/org/sagebionetworks/web/client/PortalGinInjector.java +++ b/src/main/java/org/sagebionetworks/web/client/PortalGinInjector.java @@ -136,6 +136,7 @@ import org.sagebionetworks.web.client.widget.entity.download.AddFolderDialogWidget; import org.sagebionetworks.web.client.widget.entity.download.QuizInfoDialog; import org.sagebionetworks.web.client.widget.entity.download.UploadDialogWidget; +import org.sagebionetworks.web.client.widget.entity.download.UploadDialogWidgetV2; import org.sagebionetworks.web.client.widget.entity.editor.APITableColumnConfigView; import org.sagebionetworks.web.client.widget.entity.editor.APITableConfigEditor; import org.sagebionetworks.web.client.widget.entity.editor.AttachmentConfigEditor; @@ -731,6 +732,8 @@ public interface PortalGinInjector extends Ginjector { UploadDialogWidget getUploadDialogWidget(); + UploadDialogWidgetV2 getUploadDialogWidgetV2(); + WikiMarkdownEditor getWikiMarkdownEditor(); AddFolderDialogWidget getAddFolderDialogWidget(); diff --git a/src/main/java/org/sagebionetworks/web/client/SynapseJsInteropUtils.java b/src/main/java/org/sagebionetworks/web/client/SynapseJsInteropUtils.java index c5f8f2e9e0..a9196cf13a 100644 --- a/src/main/java/org/sagebionetworks/web/client/SynapseJsInteropUtils.java +++ b/src/main/java/org/sagebionetworks/web/client/SynapseJsInteropUtils.java @@ -1,9 +1,7 @@ package org.sagebionetworks.web.client; import com.google.gwt.dom.client.Element; -import elemental2.dom.Blob; import elemental2.dom.FileList; -import org.sagebionetworks.web.client.callback.MD5Callback; public interface SynapseJsInteropUtils { FileList getFileList(String fileFieldId); diff --git a/src/main/java/org/sagebionetworks/web/client/SynapseJsInteropUtilsImpl.java b/src/main/java/org/sagebionetworks/web/client/SynapseJsInteropUtilsImpl.java index 85f2870a31..fd870da0f5 100644 --- a/src/main/java/org/sagebionetworks/web/client/SynapseJsInteropUtilsImpl.java +++ b/src/main/java/org/sagebionetworks/web/client/SynapseJsInteropUtilsImpl.java @@ -2,13 +2,11 @@ import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Element; -import elemental2.dom.Blob; import elemental2.dom.DomGlobal; import elemental2.dom.File; import elemental2.dom.FileList; import elemental2.dom.HTMLInputElement; import jsinterop.base.Js; -import org.sagebionetworks.web.client.callback.MD5Callback; public class SynapseJsInteropUtilsImpl implements SynapseJsInteropUtils { diff --git a/src/main/java/org/sagebionetworks/web/client/jsinterop/EntityUploadHandle.java b/src/main/java/org/sagebionetworks/web/client/jsinterop/EntityUploadHandle.java new file mode 100644 index 0000000000..1f9c47b5c8 --- /dev/null +++ b/src/main/java/org/sagebionetworks/web/client/jsinterop/EntityUploadHandle.java @@ -0,0 +1,14 @@ +package org.sagebionetworks.web.client.jsinterop; + +import elemental2.dom.FileList; +import jsinterop.annotations.JsPackage; +import jsinterop.annotations.JsType; + +@JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Object") +public class EntityUploadHandle { + + /** + * The EntityUploadModal component exposes an imperative handle to programmatically upload files. + */ + public native void handleUploads(FileList fileList); +} diff --git a/src/main/java/org/sagebionetworks/web/client/jsinterop/EntityUploadModalProps.java b/src/main/java/org/sagebionetworks/web/client/jsinterop/EntityUploadModalProps.java new file mode 100644 index 0000000000..783c8b7d92 --- /dev/null +++ b/src/main/java/org/sagebionetworks/web/client/jsinterop/EntityUploadModalProps.java @@ -0,0 +1,39 @@ +package org.sagebionetworks.web.client.jsinterop; + +import jsinterop.annotations.JsFunction; +import jsinterop.annotations.JsOverlay; +import jsinterop.annotations.JsPackage; +import jsinterop.annotations.JsType; + +@JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Object") +public class EntityUploadModalProps extends ReactComponentProps { + + @FunctionalInterface + @JsFunction + public interface Callback { + void run(); + } + + public String entityId; + + public boolean open; + + public Callback onClose; + + public ReactRef ref; + + @JsOverlay + public static EntityUploadModalProps create( + String containerId, + boolean open, + Callback onClose, + ReactRef ref + ) { + EntityUploadModalProps props = new EntityUploadModalProps(); + props.entityId = containerId; + props.open = open; + props.onClose = onClose; + props.ref = ref; + return props; + } +} diff --git a/src/main/java/org/sagebionetworks/web/client/jsinterop/SRC.java b/src/main/java/org/sagebionetworks/web/client/jsinterop/SRC.java index 5f9a084230..5f1253bdbb 100644 --- a/src/main/java/org/sagebionetworks/web/client/jsinterop/SRC.java +++ b/src/main/java/org/sagebionetworks/web/client/jsinterop/SRC.java @@ -132,6 +132,7 @@ public static class SynapseComponents { public static ReactComponentType< ProjectDataAvailabilityProps > ProjectDataAvailability; + public static ReactComponentType EntityUploadModal; /** * Pushes a global toast message. In SWC, you should use {@link DisplayUtils#notify}, rather than calling this method directly. diff --git a/src/main/java/org/sagebionetworks/web/client/widget/entity/controller/EntityActionControllerImpl.java b/src/main/java/org/sagebionetworks/web/client/widget/entity/controller/EntityActionControllerImpl.java index e55c785038..9aee572f89 100755 --- a/src/main/java/org/sagebionetworks/web/client/widget/entity/controller/EntityActionControllerImpl.java +++ b/src/main/java/org/sagebionetworks/web/client/widget/entity/controller/EntityActionControllerImpl.java @@ -129,6 +129,7 @@ import org.sagebionetworks.web.client.widget.entity.browse.EntityFinderWidget; import org.sagebionetworks.web.client.widget.entity.download.AddFolderDialogWidget; import org.sagebionetworks.web.client.widget.entity.download.UploadDialogWidget; +import org.sagebionetworks.web.client.widget.entity.download.UploadDialogWidgetV2; import org.sagebionetworks.web.client.widget.entity.file.AddToDownloadListV2; import org.sagebionetworks.web.client.widget.entity.menu.v3.Action; import org.sagebionetworks.web.client.widget.entity.menu.v3.ActionListener; @@ -555,6 +556,13 @@ private UploadDialogWidget getNewUploadDialogWidget() { return uploadDialogWidget; } + private UploadDialogWidgetV2 getNewUploadDialogWidgetV2() { + UploadDialogWidgetV2 uploadDialogWidgetV2 = + ginInjector.getUploadDialogWidgetV2(); + view.setUploadDialogWidget(uploadDialogWidgetV2.asWidget()); + return uploadDialogWidgetV2; + } + private WikiMarkdownEditor getWikiMarkdownEditor() { if (wikiEditor == null) { wikiEditor = ginInjector.getWikiMarkdownEditor(); @@ -2049,16 +2057,23 @@ private void postCheckCreateTableOrView(TableType table) { private void onUploadNewFileEntity() { checkUploadEntity(() -> { - UploadDialogWidget uploader = getNewUploadDialogWidget(); - uploader.configure( - DisplayConstants.TEXT_UPLOAD_FILE_OR_LINK, - null, - entityBundle.getEntity().getId(), - null, - true - ); - uploader.setUploaderLinkNameVisible(true); - uploader.show(); + if (featureFlagConfig.isFeatureEnabled(FeatureFlagKey.UPLOADER_V2)) { + UploadDialogWidgetV2 uploadDialogWidgetV2 = + getNewUploadDialogWidgetV2(); + uploadDialogWidgetV2.configure(entityBundle.getEntity().getId()); + uploadDialogWidgetV2.show(); + } else { + UploadDialogWidget uploader = getNewUploadDialogWidget(); + uploader.configure( + DisplayConstants.TEXT_UPLOAD_FILE_OR_LINK, + null, + entityBundle.getEntity().getId(), + null, + true + ); + uploader.setUploaderLinkNameVisible(true); + uploader.show(); + } }); } @@ -2122,17 +2137,23 @@ private void onUploadFile() { } private void postCheckUploadFile() { - UploadDialogWidget uploadDialogWidget = getNewUploadDialogWidget(); - uploadDialogWidget.configure( - DisplayConstants.TEXT_UPLOAD_FILE_OR_LINK, - entityBundle.getEntity(), - null, - null, - true - ); - uploadDialogWidget.disableMultipleFileUploads(); - uploadDialogWidget.setUploaderLinkNameVisible(false); - uploadDialogWidget.show(); + if (featureFlagConfig.isFeatureEnabled(FeatureFlagKey.UPLOADER_V2)) { + UploadDialogWidgetV2 uploadDialogWidgetV2 = getNewUploadDialogWidgetV2(); + uploadDialogWidgetV2.configure(entityBundle.getEntity().getId()); + uploadDialogWidgetV2.show(); + } else { + UploadDialogWidget uploadDialogWidget = getNewUploadDialogWidget(); + uploadDialogWidget.configure( + DisplayConstants.TEXT_UPLOAD_FILE_OR_LINK, + entityBundle.getEntity(), + null, + null, + true + ); + uploadDialogWidget.disableMultipleFileUploads(); + uploadDialogWidget.setUploaderLinkNameVisible(false); + uploadDialogWidget.show(); + } } private void onSubmit() { diff --git a/src/main/java/org/sagebionetworks/web/client/widget/entity/download/UploadDialogWidgetV2.java b/src/main/java/org/sagebionetworks/web/client/widget/entity/download/UploadDialogWidgetV2.java new file mode 100644 index 0000000000..f22c4f497a --- /dev/null +++ b/src/main/java/org/sagebionetworks/web/client/widget/entity/download/UploadDialogWidgetV2.java @@ -0,0 +1,81 @@ +package org.sagebionetworks.web.client.widget.entity.download; + +import com.google.gwt.event.shared.EventBus; +import com.google.gwt.user.client.ui.Widget; +import com.google.inject.Inject; +import org.sagebionetworks.web.client.GlobalApplicationState; +import org.sagebionetworks.web.client.GlobalApplicationStateImpl; +import org.sagebionetworks.web.client.context.SynapseReactClientFullContextPropsProvider; +import org.sagebionetworks.web.client.events.EntityUpdatedEvent; +import org.sagebionetworks.web.client.jsinterop.EntityUploadHandle; +import org.sagebionetworks.web.client.jsinterop.EntityUploadModalProps; +import org.sagebionetworks.web.client.jsinterop.React; +import org.sagebionetworks.web.client.jsinterop.ReactRef; +import org.sagebionetworks.web.client.jsinterop.SRC; +import org.sagebionetworks.web.client.widget.ReactComponent; + +public class UploadDialogWidgetV2 extends Widget { + + private final GlobalApplicationState globalApplicationState; + private final EventBus eventBus; + private final SynapseReactClientFullContextPropsProvider contextProvider; + + private final ReactComponent reactComponent; + + private String entityId; + private ReactRef ref; + + @Inject + public UploadDialogWidgetV2( + GlobalApplicationStateImpl globalApplicationState, + EventBus eventBus, + SynapseReactClientFullContextPropsProvider contextProvider + ) { + this.globalApplicationState = globalApplicationState; + this.eventBus = eventBus; + this.contextProvider = contextProvider; + + this.reactComponent = new ReactComponent(); + } + + public void configure(String entityId) { + this.entityId = entityId; + globalApplicationState.setDropZoneHandler(fileList -> + this.ref.current.handleUploads(fileList) + ); + + renderComponent(false); + } + + private void renderComponent(boolean open) { + this.ref = React.createRef(); + reactComponent.render( + React.createElementWithSynapseContext( + SRC.SynapseComponents.EntityUploadModal, + EntityUploadModalProps.create(entityId, open, this::onClose, this.ref), + contextProvider.getJsInteropContextProps() + ) + ); + } + + public void show() { + renderComponent(true); + } + + private void onClose() { + eventBus.fireEvent(new EntityUpdatedEvent(entityId)); + renderComponent(false); + } + + @Override + public Widget asWidget() { + return reactComponent.asWidget(); + } + + @Override + public void onUnload() { + globalApplicationState.clearDropZoneHandler(); + + super.onUnload(); + } +}