Skip to content

Commit

Permalink
Merge (#1691)
Browse files Browse the repository at this point in the history
* 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 <dsuren1@gmail.com>
  • Loading branch information
giohappy and dsuren1 authored Mar 5, 2024
1 parent 05fa301 commit 6d4eae0
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 74 deletions.
43 changes: 24 additions & 19 deletions geonode_mapstore_client/client/js/epics/gnresource.js
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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,
Expand Down
54 changes: 8 additions & 46 deletions geonode_mapstore_client/client/js/plugins/Save.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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 ? (<div
Expand All @@ -50,32 +49,15 @@ const SavePlugin = connect(
)(Save);

function SaveButton({
enabled,
onClick,
variant,
size,
loading,
className,
dirtyState: dirtyStateProp
}, { messages }) {

const dirtyState = useRef();
dirtyState.current = dirtyStateProp;
useEffect(() => {

function onBeforeUnload(event) {
if (dirtyState.current) {
(event || window.event).returnValue = null;
}
}
window.addEventListener('beforeunload', onBeforeUnload);
return () => {
window.removeEventListener('beforeunload', onBeforeUnload);
};
}, []);

return enabled
? <><Button
}) {
return (
<Button
variant={dirtyStateProp ? 'warning' : (variant || "primary")}
size={size}
onClick={() => onClick()}
Expand All @@ -84,29 +66,9 @@ function SaveButton({
>
<Message msgId="save"/>{' '}{loading && <Spinner />}
</Button>
<Prompt
when={!!dirtyStateProp}
message={(/* nextLocation, action */) => {
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,
Expand All @@ -126,7 +88,7 @@ const ConnectedSaveButton = connect(
{
onClick: saveDirectContent
}
)((withRouter(SaveButton)));
)((withRouter(withPrompt(SaveButton))));

export default createPlugin('Save', {
component: SavePlugin,
Expand Down
22 changes: 14 additions & 8 deletions geonode_mapstore_client/client/js/plugins/SaveAs.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -107,24 +108,24 @@ const SaveAsPlugin = connect(
)(SaveAs);

function SaveAsButton({
enabled,
onClick,
variant,
size,
resource,
dirtyState,
disabled
}) {
return enabled
? <Button
variant={variant || "primary"}

return (
<Button
variant={dirtyState ? 'warning' : (variant || "primary")}
size={size}
disabled={disabled}
onClick={() => onClick([ resource ])}
>
<Message msgId="saveAs"/>
</Button>
: null
;
);
}

const canCopyResourceFunction = (state) => {
Expand All @@ -142,6 +143,10 @@ const canCopyResourceFunction = (state) => {
};
};

const isDisabledByDirtyState = (dirtyState) => {
return typeof dirtyState === 'object' ? !!dirtyState : false;
};

const ConnectedSaveAsButton = connect(
createSelector(
getResourceData,
Expand All @@ -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,
Expand Down
49 changes: 49 additions & 0 deletions geonode_mapstore_client/client/js/plugins/save/withPrompt.jsx
Original file line number Diff line number Diff line change
@@ -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
? <><Component {...props}/>
<Prompt
when={!!props.dirtyState}
message={(/* nextLocation, action */) => {
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;
};
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ import {
updatingThumbnailResource,
isThumbnailChanged,
canEditPermissions,
canManageResourcePermissions
canManageResourcePermissions,
isNewMapViewerResource
} from '../resource';
import { ResourceTypes } from '@js/utils/ResourceUtils';

const testState = {
gnresource: {
Expand Down Expand Up @@ -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();
});
});
9 changes: 9 additions & 0 deletions geonode_mapstore_client/client/js/selectors/resource.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 6d4eae0

Please sign in to comment.