From 6d4eae0d0899968e27c80e6b598cbeceb08cd084 Mon Sep 17 00:00:00 2001 From: Giovanni Allegri Date: Tue, 5 Mar 2024 16:37:46 +0100 Subject: [PATCH] Merge (#1691) * Fix - Duplicated API requests on viewer resource load (#1676) * Enhance link system between map and mapviewers (#1677) * Configuration update for the plugins available inside the context creator (#1675) * #1683: Enable the 3D plugin in the default GeoNode map configuration (#1684) * #1685 - Fix: Mapviewer doesn't show configure plugin steps (#1686) * #1685 - Fix: Mapviewer doesn't show configure plugin steps * Refresh map on unlink viewer resource * #1689: Add a warning when closing an unsaved map viewer (#1690) --------- Co-authored-by: Suren --- .../client/js/epics/gnresource.js | 43 ++++++++------- .../client/js/plugins/Save.jsx | 54 +++---------------- .../client/js/plugins/SaveAs.jsx | 22 +++++--- .../client/js/plugins/save/withPrompt.jsx | 49 +++++++++++++++++ .../js/selectors/__tests__/resource-test.js | 10 +++- .../client/js/selectors/resource.js | 9 ++++ 6 files changed, 113 insertions(+), 74 deletions(-) create mode 100644 geonode_mapstore_client/client/js/plugins/save/withPrompt.jsx diff --git a/geonode_mapstore_client/client/js/epics/gnresource.js b/geonode_mapstore_client/client/js/epics/gnresource.js index fd49f9b1af..01e24270c4 100644 --- a/geonode_mapstore_client/client/js/epics/gnresource.js +++ b/geonode_mapstore_client/client/js/epics/gnresource.js @@ -396,7 +396,7 @@ const resourceTypes = { const { response } = payload; const { success, error: [error] } = response; if (success) { - window.location.reload(); + window.location.replace(window.location.href); return Observable.empty(); } return Observable.throw(new Error(error)); @@ -595,32 +595,37 @@ export const gnManageLinkedResource = (action$, store) => .switchMap((action) => { const state = store.getState(); const resource = state.gnresource ?? {}; + const params = state?.gnresource?.params; const { source, target, resourceType, processType } = action.payload; + const isLinkResource = processType === ProcessTypes.LINK_RESOURCE; const resourceObservable = resourceTypes[resourceType]; let observable$ = resourceObservable?.linkedResourceObservable; let linkedResourceFn = setLinkedResourcesByPk; - if (processType === ProcessTypes.REMOVE_LINKED_RESOURCE) { + if (!isLinkResource) { observable$ = resourceObservable?.removeLinkedResourceObservable; linkedResourceFn = removeLinkedResourcesByPk; } if (!observable$) Observable.empty(); - return Observable.defer(() => linkedResourceFn(source, target)) - .switchMap((response) => - Observable.concat( - observable$({response, source, resource}), - Observable.of( - successNotification({ - title: "gnviewer.linkedResource.title", - message: `gnviewer.linkedResource.message.success.${processType}`} - )) - ).catch(() => Observable.of(errorNotification({ - title: "gnviewer.linkedResource.title", - message: `gnviewer.linkedResource.message.failure.${processType}` - })))) - .let(wrapStartStop( - setControlProperty(processType, 'loading', true), - setControlProperty(processType, 'loading', false) - )); + return Observable.concat( + ...(isLinkResource ? [Observable.of(setResourcePathParameters({ ...params, pk: target}))] : []), + Observable.defer(() => linkedResourceFn(source, target)) + .switchMap((response) => + Observable.concat( + observable$({response, source, resource}), + Observable.of( + successNotification({ + title: "gnviewer.linkedResource.title", + message: `gnviewer.linkedResource.message.success.${processType}`} + )) + ).catch(() => Observable.of(errorNotification({ + title: "gnviewer.linkedResource.title", + message: `gnviewer.linkedResource.message.failure.${processType}` + })))) + .let(wrapStartStop( + setControlProperty(processType, 'loading', true), + setControlProperty(processType, 'loading', false) + )) + ); }); export default { gnViewerRequestNewResourceConfig, diff --git a/geonode_mapstore_client/client/js/plugins/Save.jsx b/geonode_mapstore_client/client/js/plugins/Save.jsx index 3bf007b629..80a3e5a071 100644 --- a/geonode_mapstore_client/client/js/plugins/Save.jsx +++ b/geonode_mapstore_client/client/js/plugins/Save.jsx @@ -6,8 +6,7 @@ * LICENSE file in the root directory of this source tree. */ -import React, { useRef, useEffect } from 'react'; -import PropTypes from 'prop-types'; +import React from 'react'; import { connect } from 'react-redux'; import { createSelector } from 'reselect'; import { createPlugin } from '@mapstore/framework/utils/PluginsUtils'; @@ -28,8 +27,8 @@ import { getResourceDirtyState } from '@js/selectors/resource'; import { getCurrentResourcePermissionsLoading } from '@js/selectors/resourceservice'; -import { withRouter, Prompt } from 'react-router'; -import { getMessageById } from '@mapstore/framework/utils/LocaleUtils'; +import { withRouter } from 'react-router'; +import withPrompt from '@js/plugins/save/withPrompt'; function Save(props) { return props.saving ? (
{ - - function onBeforeUnload(event) { - if (dirtyState.current) { - (event || window.event).returnValue = null; - } - } - window.addEventListener('beforeunload', onBeforeUnload); - return () => { - window.removeEventListener('beforeunload', onBeforeUnload); - }; - }, []); - - return enabled - ? <> - { - const confirmed = window.confirm(getMessageById(messages, 'gnviewer.prompPendingChanges')); // eslint-disable-line no-alert - // if confirm the path should be the next one - if (confirmed) { - return true; - } - // currently it's not possible to replace the pathname - // without side effect - // such as reloading of the page - return false; - }} - /> - - : null - ; + ); } -SaveButton.contextTypes = { - messages: PropTypes.object -}; - const ConnectedSaveButton = connect( createSelector( isLoggedIn, @@ -126,7 +88,7 @@ const ConnectedSaveButton = connect( { onClick: saveDirectContent } -)((withRouter(SaveButton))); +)((withRouter(withPrompt(SaveButton)))); export default createPlugin('Save', { component: SavePlugin, diff --git a/geonode_mapstore_client/client/js/plugins/SaveAs.jsx b/geonode_mapstore_client/client/js/plugins/SaveAs.jsx index 48e3b72d78..563a642649 100644 --- a/geonode_mapstore_client/client/js/plugins/SaveAs.jsx +++ b/geonode_mapstore_client/client/js/plugins/SaveAs.jsx @@ -37,6 +37,7 @@ import { processResources } from '@js/actions/gnresource'; import { getCurrentResourceCopyLoading } from '@js/selectors/resourceservice'; import Dropdown from '@js/components/Dropdown'; import FaIcon from '@js/components/FaIcon'; +import withPrompt from '@js/plugins/save/withPrompt'; function SaveAs({ resources, @@ -107,24 +108,24 @@ const SaveAsPlugin = connect( )(SaveAs); function SaveAsButton({ - enabled, onClick, variant, size, resource, + dirtyState, disabled }) { - return enabled - ? - : null - ; + ); } const canCopyResourceFunction = (state) => { @@ -142,6 +143,10 @@ const canCopyResourceFunction = (state) => { }; }; +const isDisabledByDirtyState = (dirtyState) => { + return typeof dirtyState === 'object' ? !!dirtyState : false; +}; + const ConnectedSaveAsButton = connect( createSelector( getResourceData, @@ -150,13 +155,14 @@ const ConnectedSaveAsButton = connect( (resource, dirtyState, canCopy) => ({ enabled: !!canCopy(resource), resource, - disabled: !!dirtyState + dirtyState: !isDisabledByDirtyState(dirtyState) && !!dirtyState, + disabled: isDisabledByDirtyState(dirtyState) }) ), { onClick: setControlProperty.bind(null, ProcessTypes.COPY_RESOURCE, 'value') } -)((SaveAsButton)); +)(withPrompt(SaveAsButton)); function CopyMenuItem({ resource, diff --git a/geonode_mapstore_client/client/js/plugins/save/withPrompt.jsx b/geonode_mapstore_client/client/js/plugins/save/withPrompt.jsx new file mode 100644 index 0000000000..27bf250934 --- /dev/null +++ b/geonode_mapstore_client/client/js/plugins/save/withPrompt.jsx @@ -0,0 +1,49 @@ +import React, { useRef, useEffect } from 'react'; +import PropTypes from 'prop-types'; +import { getMessageById } from '@mapstore/framework/utils/LocaleUtils'; +import { Prompt } from 'react-router-dom'; + + +export default (Component) => { + const PromptComponent = (props, {messages}) => { + const dirtyState = useRef(); + dirtyState.current = props.dirtyState; + useEffect(() => { + function onBeforeUnload(event) { + if (dirtyState.current) { + (event || window.event).returnValue = null; + } + } + window.addEventListener('beforeunload', onBeforeUnload); + return () => { + window.removeEventListener('beforeunload', onBeforeUnload); + }; + }, []); + + return props.enabled + ? <> + { + const confirmed = window.confirm(getMessageById(messages, 'gnviewer.prompPendingChanges')); // eslint-disable-line no-alert + // if confirm the path should be the next one + if (confirmed) { + return true; + } + window.history.back(); // to return back to previous path + // currently it's not possible to replace the pathname + // without side effect + // such as reloading of the page + return false; + }} + /> + + : null + ; + }; + + PromptComponent.contextTypes = { + messages: PropTypes.object + }; + return PromptComponent; +}; diff --git a/geonode_mapstore_client/client/js/selectors/__tests__/resource-test.js b/geonode_mapstore_client/client/js/selectors/__tests__/resource-test.js index 6d5df4080b..7237e33a51 100644 --- a/geonode_mapstore_client/client/js/selectors/__tests__/resource-test.js +++ b/geonode_mapstore_client/client/js/selectors/__tests__/resource-test.js @@ -16,8 +16,10 @@ import { updatingThumbnailResource, isThumbnailChanged, canEditPermissions, - canManageResourcePermissions + canManageResourcePermissions, + isNewMapViewerResource } from '../resource'; +import { ResourceTypes } from '@js/utils/ResourceUtils'; const testState = { gnresource: { @@ -89,4 +91,10 @@ describe('resource selector', () => { expect(canManageResourcePermissions(state)).toBeFalsy(); state.gnresource.data.perms = undefined; }); + it('test isNewMapViewerResource', () => { + let state = {...testState, gnresource: {...testState.gnresource, type: ResourceTypes.VIEWER, params: {pk: "new"}}}; + expect(isNewMapViewerResource(state)).toBeTruthy(); + state.gnresource.params.pk = '1'; + expect(isNewMapViewerResource(state)).toBeFalsy(); + }); }); diff --git a/geonode_mapstore_client/client/js/selectors/resource.js b/geonode_mapstore_client/client/js/selectors/resource.js index ec0d44512a..d2ec27c3e6 100644 --- a/geonode_mapstore_client/client/js/selectors/resource.js +++ b/geonode_mapstore_client/client/js/selectors/resource.js @@ -253,7 +253,16 @@ function isResourceDataEqual(state, initialData = {}, currentData = {}) { } } +export const isNewMapViewerResource = (state) => { + const isNew = state?.gnresource?.params?.pk === "new"; + const isMapViewer = state?.gnresource?.type === ResourceTypes.VIEWER; + return isNew && isMapViewer; +}; + export const getResourceDirtyState = (state) => { + if (isNewMapViewerResource(state)) { + return true; + } const canEdit = canEditPermissions(state); const isDeleting = getCurrentResourceDeleteLoading(state); const isCopying = getCurrentResourceCopyLoading(state);