From f6f1e1ca6c3ac7d6507eeac9e92f6dc1a4a6858a Mon Sep 17 00:00:00 2001 From: Jay Hodgson Date: Tue, 26 Nov 2024 09:58:43 -0800 Subject: [PATCH] integrate react file browser (in experimental mode) --- .../web/client/FeatureFlagKey.java | 3 + .../jsinterop/EntityFileBrowserProps.java | 31 ++++++++++ .../web/client/jsinterop/SRC.java | 1 + .../entity/browse/FilesBrowserViewImpl.java | 61 ++++++++++++++++--- .../entity/browse/FilesBrowserViewImpl.ui.xml | 2 + 5 files changed, 89 insertions(+), 9 deletions(-) create mode 100644 src/main/java/org/sagebionetworks/web/client/jsinterop/EntityFileBrowserProps.java diff --git a/src/main/java/org/sagebionetworks/web/client/FeatureFlagKey.java b/src/main/java/org/sagebionetworks/web/client/FeatureFlagKey.java index 04eb963931..35b1e487d8 100644 --- a/src/main/java/org/sagebionetworks/web/client/FeatureFlagKey.java +++ b/src/main/java/org/sagebionetworks/web/client/FeatureFlagKey.java @@ -43,6 +43,9 @@ public enum FeatureFlagKey { // If enabled, uses the v2 uploader (react implementation) for file entity uploads. UPLOADER_V2("UPLOADER_V2"), + // If enabled, uses the file browser react implementation + REACT_FILE_BROWSER("REACT_FILE_BROWSER"), + // Last flag is used only for tests TEST_FLAG_ONLY("TEST_FLAG_ONLY"); diff --git a/src/main/java/org/sagebionetworks/web/client/jsinterop/EntityFileBrowserProps.java b/src/main/java/org/sagebionetworks/web/client/jsinterop/EntityFileBrowserProps.java new file mode 100644 index 0000000000..70d5f4de9d --- /dev/null +++ b/src/main/java/org/sagebionetworks/web/client/jsinterop/EntityFileBrowserProps.java @@ -0,0 +1,31 @@ +package org.sagebionetworks.web.client.jsinterop; + +import jsinterop.annotations.JsFunction; +import jsinterop.annotations.JsOverlay; +import jsinterop.annotations.JsPackage; +import jsinterop.annotations.JsType; +import org.sagebionetworks.web.client.jsni.ReferenceJSNIObject; + +@JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Object") +public class EntityFileBrowserProps extends ReactComponentProps { + + @FunctionalInterface + @JsFunction + public interface OnSelectCallback { + void run(ReferenceJSNIObject selected); + } + + OnSelectCallback onSelect; + String parentContainerId; + + @JsOverlay + public static EntityFileBrowserProps create( + String parentContainerId, + OnSelectCallback onSelect + ) { + EntityFileBrowserProps props = new EntityFileBrowserProps(); + props.parentContainerId = parentContainerId; + props.onSelect = onSelect; + 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 790f20bb47..cd8cb47efe 100644 --- a/src/main/java/org/sagebionetworks/web/client/jsinterop/SRC.java +++ b/src/main/java/org/sagebionetworks/web/client/jsinterop/SRC.java @@ -21,6 +21,7 @@ public static class SynapseComponents { public static ReactComponentType EntityBadgeIcons; public static ReactComponentType DatasetItemsEditor; public static ReactComponentType EntityFinder; + public static ReactComponentType EntityFileBrowser; public static ReactComponentType EvaluationCard; public static ReactComponentType< EvaluationEditorPageProps diff --git a/src/main/java/org/sagebionetworks/web/client/widget/entity/browse/FilesBrowserViewImpl.java b/src/main/java/org/sagebionetworks/web/client/widget/entity/browse/FilesBrowserViewImpl.java index 0d3df2531b..09169236c8 100644 --- a/src/main/java/org/sagebionetworks/web/client/widget/entity/browse/FilesBrowserViewImpl.java +++ b/src/main/java/org/sagebionetworks/web/client/widget/entity/browse/FilesBrowserViewImpl.java @@ -1,20 +1,23 @@ package org.sagebionetworks.web.client.widget.entity.browse; +import com.google.gwt.core.client.GWT; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.ui.IsWidget; import com.google.gwt.user.client.ui.Widget; import com.google.inject.Inject; -import org.gwtbootstrap3.client.ui.AnchorListItem; -import org.gwtbootstrap3.client.ui.Button; import org.gwtbootstrap3.client.ui.Heading; -import org.gwtbootstrap3.client.ui.Tooltip; import org.gwtbootstrap3.client.ui.html.Div; -import org.gwtbootstrap3.client.ui.html.Span; import org.sagebionetworks.web.client.DisplayUtils; -import org.sagebionetworks.web.client.security.AuthenticationController; +import org.sagebionetworks.web.client.FeatureFlagConfig; +import org.sagebionetworks.web.client.FeatureFlagKey; +import org.sagebionetworks.web.client.context.SynapseReactClientFullContextPropsProvider; +import org.sagebionetworks.web.client.jsinterop.EntityFileBrowserProps; +import org.sagebionetworks.web.client.jsinterop.React; +import org.sagebionetworks.web.client.jsinterop.ReactElement; +import org.sagebionetworks.web.client.jsinterop.SRC; import org.sagebionetworks.web.client.utils.CallbackP; -import org.sagebionetworks.web.client.widget.HelpWidget; +import org.sagebionetworks.web.client.widget.ReactComponent; public class FilesBrowserViewImpl implements FilesBrowserView { @@ -23,6 +26,8 @@ public interface FilesBrowserViewImplUiBinder private EntityTreeBrowser entityTreeBrowser; private Widget widget; + SynapseReactClientFullContextPropsProvider propsProvider; + private FeatureFlagConfig featureFlagConfig; @UiField Div files; @@ -39,12 +44,21 @@ public interface FilesBrowserViewImplUiBinder @UiField Heading title; + @UiField + Div reactFileBrowserContainer; + + CallbackP entityClickedCallback; + @Inject public FilesBrowserViewImpl( FilesBrowserViewImplUiBinder binder, - EntityTreeBrowser entityTreeBrowser + EntityTreeBrowser entityTreeBrowser, + SynapseReactClientFullContextPropsProvider propsProvider, + FeatureFlagConfig featureFlagConfig ) { widget = binder.createAndBindUi(this); + this.featureFlagConfig = featureFlagConfig; + this.propsProvider = propsProvider; this.entityTreeBrowser = entityTreeBrowser; Widget etbW = entityTreeBrowser.asWidget(); etbW.addStyleName("margin-top-10"); @@ -53,15 +67,44 @@ public FilesBrowserViewImpl( @Override public void configure(String entityId) { + boolean isReactFileBrowser = featureFlagConfig.isFeatureEnabled( + FeatureFlagKey.REACT_FILE_BROWSER + ); title.setVisible(false); - entityTreeBrowser.configure(entityId); + reactFileBrowserContainer.setVisible(isReactFileBrowser); + files.setVisible(!isReactFileBrowser); + if (isReactFileBrowser) { + rerenderFileBrowser(entityId); + } else { + entityTreeBrowser.configure(entityId); + } + } + + public void rerenderFileBrowser(String parentContainerId) { + reactFileBrowserContainer.clear(); + ReactComponent componentContainer = new ReactComponent(); + EntityFileBrowserProps props = EntityFileBrowserProps.create( + parentContainerId, + ref -> { + String entityId = ref.getTargetId(); + entityClickedCallback.invoke(entityId); + } + ); + ReactElement component = React.createElementWithSynapseContext( + SRC.SynapseComponents.EntityFileBrowser, + props, + propsProvider.getJsInteropContextProps() + ); + reactFileBrowserContainer.add(componentContainer); + componentContainer.render(component); } @Override public void setEntityClickedHandler(CallbackP callback) { + this.entityClickedCallback = callback; entityTreeBrowser.setEntityClickedHandler(entityId -> { entityTreeBrowser.setLoadingVisible(true); - callback.invoke(entityId); + entityClickedCallback.invoke(entityId); }); } diff --git a/src/main/resources/org/sagebionetworks/web/client/widget/entity/browse/FilesBrowserViewImpl.ui.xml b/src/main/resources/org/sagebionetworks/web/client/widget/entity/browse/FilesBrowserViewImpl.ui.xml index 74f62cd613..d293fbbf8a 100755 --- a/src/main/resources/org/sagebionetworks/web/client/widget/entity/browse/FilesBrowserViewImpl.ui.xml +++ b/src/main/resources/org/sagebionetworks/web/client/widget/entity/browse/FilesBrowserViewImpl.ui.xml @@ -4,6 +4,7 @@ xmlns:ui="urn:ui:com.google.gwt.uibinder" xmlns:b="urn:import:org.gwtbootstrap3.client.ui" xmlns:bh="urn:import:org.gwtbootstrap3.client.ui.html" + xmlns:w="urn:import:org.sagebionetworks.web.client.widget" > +