Skip to content

Commit

Permalink
Added table of content to open layer details in map view. (GeoNode#1661
Browse files Browse the repository at this point in the history
…) (GeoNode#1917)

* Added table of content to open layer details in map view. Fixes issue#1660.

* Updated to the newer version.

* Code refactor

* Fixed instability issues for info button on TOC.

* Fixed linked resources for info button on TOC and code cleanup.

* Avoided repeating calls to get layer dataset fields including linked resources for info button on TOC.

* Fixed localConfig json file for the info button.

* Reverted api changes to include linked resources, because they are already included for 'viewer_common' api_preset.

* Fixed getDatasetByPk definition.

* Fixed linting issues.

* Fixed bug where info button was misbehaving when any maplayer had no dataset.

* Fixed a bug where the title of the dataset was not changing on the details panel.

---------

Co-authored-by: Suren <[email protected]>
(cherry picked from commit 9b1dcb4)

Co-authored-by: ahmdthr <[email protected]>
  • Loading branch information
2 people authored and ridoo committed Dec 4, 2024
1 parent a45be1e commit 602970b
Show file tree
Hide file tree
Showing 10 changed files with 303 additions and 97 deletions.
22 changes: 22 additions & 0 deletions geonode_mapstore_client/client/js/actions/gnresource.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ export const SET_MAP_VIEWER_LINKED_RESOURCE = 'GEONODE:SET_MAP_VIEWER_LINKED_RES
export const MANAGE_LINKED_RESOURCE = 'GEONODE:MANAGE_LINKED_RESOURCE';
export const SET_DEFAULT_VIEWER_PLUGINS = 'GEONODE:SET_DEFAULT_VIEWER_PLUGINS';
export const SET_SELECTED_LAYER = 'GEONODE:SET_SELECTED_LAYER';
export const UPDATE_LAYER_DATASET = 'GEONODE:UPDATE_LAYER_DATASET';
export const SET_SELECTED_LAYER_DATASET = 'GEONODE:SET_SELECTED_LAYER_DATASET';

/**
* Actions for GeoNode resource
Expand Down Expand Up @@ -398,3 +400,23 @@ export function setSelectedLayer(layer) {
layer
};
}

/**
* Update layer dataset
*/
export function updateLayerDataset(layer) {
return {
type: UPDATE_LAYER_DATASET,
layer
};
}

/**
* Get layer dataset
*/
export function setLayerDataset(layerId) {
return {
type: SET_SELECTED_LAYER_DATASET,
layerId
};
}
2 changes: 2 additions & 0 deletions geonode_mapstore_client/client/js/apps/gn-catalogue.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import {
import { CATALOGUE_ROUTES, appRouteComponentTypes } from '@js/utils/AppRoutesUtils';
import { updateGeoNodeSettings } from '@js/actions/gnsettings';
import {
gnFetchMissingLayerData,
gnCheckSelectedDatasetPermissions,
gnSetDatasetsPermissions,
// to make the current layout work we need this epic
Expand Down Expand Up @@ -140,6 +141,7 @@ getEndpoints()
const appEpics = cleanEpics({
...standardEpics,
...configEpics,
gnFetchMissingLayerData,
gnCheckSelectedDatasetPermissions,
gnSetDatasetsPermissions,
...pluginsDefinition.epics,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const EditTitle = ({ title, onEdit, disabled }) => {
setTextValue(evt.target.value);
onEdit(evt.target.value);
}}
value={textValue}
value={onEdit ? textValue : title}
disabled={disabled}
/>
</div>);
Expand Down
36 changes: 34 additions & 2 deletions geonode_mapstore_client/client/js/epics/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ import Rx from "rxjs";
import { setEditPermissionStyleEditor, INIT_STYLE_SERVICE } from "@mapstore/framework/actions/styleeditor";
import { getSelectedLayer, layersSelector } from "@mapstore/framework/selectors/layers";
import { getConfigProp } from "@mapstore/framework/utils/ConfigUtils";
import { getDatasetByName, getDatasetsByName, getResourceByTypeAndByPk } from '@js/api/geonode/v2';
import { getDatasetByName, getDatasetsByName, getDatasetByPk } from '@js/api/geonode/v2';
import { MAP_CONFIG_LOADED } from '@mapstore/framework/actions/config';
import { setPermission } from '@mapstore/framework/actions/featuregrid';
import { SELECT_NODE, updateNode, ADD_LAYER } from '@mapstore/framework/actions/layers';
import { setSelectedDatasetPermissions, setSelectedLayer } from '@js/actions/gnresource';
import { setSelectedDatasetPermissions, setSelectedLayer, updateLayerDataset, setLayerDataset } from '@js/actions/gnresource';
import { updateMapLayoutEpic as msUpdateMapLayoutEpic } from '@mapstore/framework/epics/maplayout';
import isEmpty from 'lodash/isEmpty';

// We need to include missing epics. The plugins that normally include this epic is not used.

Expand Down Expand Up @@ -56,6 +57,37 @@ export const gnCheckSelectedDatasetPermissions = (action$, { getState } = {}) =>
);
});

/**
* Fetches missing values for selected layers
*/
export const gnFetchMissingLayerData = (action$, { getState } = {}) =>
action$.ofType(SELECT_NODE)
.filter(({ nodeType }) => nodeType && nodeType === "layer")
.switchMap(() => {
const state = getState() || {};
const layer = getSelectedLayer(state);
const layerResourceId = layer?.extendedParams?.pk;
const layerResourceDataset = state.gnresource.data?.maplayers?.find(mapLayer => mapLayer.dataset?.pk === parseInt(layerResourceId, 10))?.dataset;
return layerResourceDataset
? isEmpty(layerResourceDataset?.linkedResources)
? Rx.Observable.defer(() =>
getDatasetByPk(layerResourceId)
.then((layerDataset) => layerDataset)
.catch(() => [])
).switchMap((layerDataset) =>
Rx.Observable.of(
updateLayerDataset(layerDataset),
setLayerDataset(layerResourceId)
)
)
: Rx.Observable.of(
setLayerDataset(layerResourceId)
)
: Rx.Observable.of(
setLayerDataset()
)
});


/**
* Checks the permissions for layers when a map is loaded and when a new layer is added
Expand Down
112 changes: 25 additions & 87 deletions geonode_mapstore_client/client/js/plugins/LayerDetailViewer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,102 +12,65 @@ import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { getConfigProp } from '@mapstore/framework/utils/ConfigUtils';
import DetailsPanel from '@js/components/DetailsPanel';
import { userSelector } from '@mapstore/framework/selectors/security';
import {
editTitleResource,
editAbstractResource,
editThumbnailResource,
setFavoriteResource,
setMapThumbnail,
setResourceThumbnail,
enableMapThumbnailViewer,
downloadResource
} from '@js/actions/gnresource';
import { processingDownload } from '@js/selectors/resourceservice';
import { enableMapThumbnailViewer } from '@js/actions/gnresource';
import FaIcon from '@js/components/FaIcon/FaIcon';
import controls from '@mapstore/framework/reducers/controls';
import { setControlProperty } from '@mapstore/framework/actions/controls';
import gnresource from '@js/reducers/gnresource';
import gnsearch from '@js/reducers/gnsearch';
import {
canEditResource,
getResourceId,
isThumbnailChanged,
updatingThumbnailResource
} from '@js/selectors/resource';
import {
getUpdatedLayer
} from '@mapstore/framework/selectors/styleeditor';
import { getSelectedLayerDataset } from '@js/selectors/resource';
import GNButton from '@js/components/Button';
import useDetectClickOut from '@js/hooks/useDetectClickOut';
import OverlayContainer from '@js/components/OverlayContainer';
import { withRouter } from 'react-router';
import { hashLocationToHref } from '@js/utils/SearchUtils';
import Message from '@mapstore/framework/components/I18N/Message';
import { mapSelector } from '@mapstore/framework/selectors/map';
import { resourceHasPermission } from '@js/utils/ResourceUtils';
import { parsePluginConfigExpressions } from '@js/utils/MenuUtils';
import tooltip from '@mapstore/framework/components/misc/enhancers/tooltip';
import layerDetailViewerEpics from '@js/epics/layerdetailviewer';
import tabComponents from '@js/plugins/detailviewer/tabComponents';

const Button = tooltip(GNButton);

const ConnectedDetailsPanel = connect(
createSelector([
state => state?.gnresource?.layerDataset || null,
state => state?.gnresource?.selectedLayerDataset || null,
state => state?.gnresource?.loading || false,
state => state?.gnresource?.layerDataset?.favorite || false,
state => state?.gnsave?.savingThumbnailMap || false,
isThumbnailChanged,
updatingThumbnailResource,
mapSelector,
state => state?.gnresource?.showMapThumbnail || false,
processingDownload
], (resource, loading, favorite, savingThumbnailMap, thumbnailChanged, resourceThumbnailUpdating, mapData, showMapThumbnail, downloading) => ({
state => state?.gnresource?.showMapThumbnail || false
], (resource, loading, mapData, showMapThumbnail) => ({
resource,
loading,
savingThumbnailMap,
favorite: favorite,
isThumbnailChanged: thumbnailChanged,
resourceThumbnailUpdating,
initialBbox: mapData?.bbox,
enableMapViewer: showMapThumbnail,
downloading,
canDownload: resourceHasPermission(resource, 'download_resourcebase'),
resourceId: resource.pk
resourceId: resource?.pk,
tabComponents
})),
{
closePanel: setControlProperty.bind(null, 'rightOverlay', 'enabled', false),
onFavorite: setFavoriteResource,
onMapThumbnail: setMapThumbnail,
onResourceThumbnail: setResourceThumbnail,
onClose: enableMapThumbnailViewer,
onAction: downloadResource
onClose: enableMapThumbnailViewer
}
)(DetailsPanel);

const ButtonViewer = ({ onClick, layer, size, status, showMessage }) => {
const layerResourceId = layer?.extendedParams?.pk;
const ButtonViewer = ({ onClick, layer, size, status }) => {
const layerResourceId = layer?.pk;
const handleClickButton = () => {
onClick();
};

return layerResourceId && status === 'LAYER' ? (
<Button
variant="primary"
className="square-button-md"
size={size}
onClick={handleClickButton}
tooltipId={<Message msgId={`gnviewer.info`} />}
>
{!showMessage ? <FaIcon name="info-circle" /> : <Message msgId="gnviewer.editInfo"/>}
<FaIcon name={'info-circle'} />
</Button>
) : null;
};

const ConnectedButton = connect(
createSelector([getUpdatedLayer], (layer) => ({
layer: layer
createSelector([
getSelectedLayerDataset
], (layer) => ({
layer
})),
{
onClick: setControlProperty.bind(
Expand All @@ -122,29 +85,13 @@ const ConnectedButton = connect(
function LayerDetailViewer({
location,
enabled,
onEditResource,
onEditAbstractResource,
onEditThumbnail,
canEdit,
user,
onClose,
monitoredState,
queryPathname = '/',
tabs = []
}) {
const parsedConfig = parsePluginConfigExpressions(monitoredState, { tabs });

const handleTitleValue = (val) => {
onEditResource(val);
};

const handleAbstractValue = (val) => {
onEditAbstractResource(val);
};
const handleEditThumbnail = (val) => {
onEditThumbnail(val, true);
};

const node = useDetectClickOut({
disabled: !enabled,
onClickOut: () => {
Expand All @@ -166,11 +113,11 @@ function LayerDetailViewer({
className="gn-overlay-wrapper"
>
<ConnectedDetailsPanel
editTitle={handleTitleValue}
editAbstract={handleAbstractValue}
editThumbnail={handleEditThumbnail}
activeEditMode={enabled && canEdit}
enableFavorite={!!user}
editTitle={null}
editAbstract={null}
editThumbnail={() => {}}
activeEditMode={false}
enableFavorite={false}
formatHref={handleFormatHref}
tabs={parsedConfig.tabs}
pathname={queryPathname}
Expand All @@ -184,24 +131,16 @@ const LayerDetailViewerPlugin = connect(
[
(state) =>
state?.controls?.rightOverlay?.enabled === 'LayerDetailViewer',
canEditResource,
getUpdatedLayer,
getResourceId,
userSelector,
getSelectedLayerDataset,
state => getMonitoredState(state, getConfigProp('monitorState'))
],
(enabled, canEdit, layer, user, monitoredState) => ({
(enabled, layer, monitoredState) => ({
enabled,
canEdit,
layer,
user,
monitoredState
})
),
{
onEditResource: editTitleResource,
onEditAbstractResource: editAbstractResource,
onEditThumbnail: editThumbnailResource,
onClose: setControlProperty.bind(null, 'rightOverlay', 'enabled', false)
}
)(withRouter(LayerDetailViewer));
Expand All @@ -211,13 +150,12 @@ export default createPlugin('LayerDetailViewer', {
containers: {
TOC: {
target: 'toolbar',
name: 'LayerDetailViewerButton',
Component: ConnectedButton
}
},
epics: layerDetailViewerEpics,
reducers: {
gnresource,
gnsearch,
controls
}
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2024, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

import { connect } from 'react-redux';
import DetailsLocations from '@js/components/DetailsPanel/DetailsLocations';
import DetailsAssets from '@js/components/DetailsPanel/DetailsAssets';
import DetailsAttributeTable from '@js/components/DetailsPanel/DetailsAttributeTable';
import DetailsLinkedResources from '@js/components/DetailsPanel/DetailsLinkedResources';
import DetailsSettings from '@js/components/DetailsPanel/DetailsSettings';
import { updateResourceProperties } from '@js/actions/gnresource';

const tabComponents = {
'attribute-table': DetailsAttributeTable,
'linked-resources': DetailsLinkedResources,
'locations': DetailsLocations,
'assets': DetailsAssets,
'settings': connect(() => ({}), { onChange: updateResourceProperties })(DetailsSettings)
};

export default tabComponents;
8 changes: 4 additions & 4 deletions geonode_mapstore_client/client/js/plugins/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,10 @@ export const plugins = {
'VisualStyleEditor',
() => import(/* webpackChunkName: 'plugins/visual-style-editor-plugin' */ '@js/plugins/VisualStyleEditor')
),
LayerDetailViewerPlugin: toModulePlugin(
'LayerDetailViewer',
() => import(/* webpackChunkName: 'plugins/detail-viewer-plugin' */ '@js/plugins/LayerDetailViewer')
),
LegendPlugin: toModulePlugin(
'Legend',
() => import(/* webpackChunkName: 'plugins/legend-plugin' */ '@js/plugins/Legend')
Expand Down Expand Up @@ -436,10 +440,6 @@ export const plugins = {
'Settings',
() => import(/* webpackChunkName: 'plugins/settings' */ '@mapstore/framework/plugins/Settings')
),
LayerDetailViewerPlugin: toModulePlugin(
'LayerDetailViewer',
() => import(/* webpackChunkName: 'plugins/detail-viewer-plugin' */ '@js/plugins/LayerDetailViewer')
),
DataCiteDownloadPlugin: toModulePlugin(
'DataCiteDownload',
() => import('@js/plugins/downloads/DataCiteDownload')
Expand Down
Loading

0 comments on commit 602970b

Please sign in to comment.