diff --git a/geonode_mapstore_client/client/js/actions/gnresource.js b/geonode_mapstore_client/client/js/actions/gnresource.js index 0fa34353c2..b232a98f5a 100644 --- a/geonode_mapstore_client/client/js/actions/gnresource.js +++ b/geonode_mapstore_client/client/js/actions/gnresource.js @@ -34,6 +34,8 @@ export const ENABLE_MAP_THUMBNAIL_VIEWER = 'GEONODE_ENABLE_MAP_THUMBNAIL_VIEWER' export const DOWNLOAD_RESOURCE = 'GEONODE_DOWNLOAD_RESOURCE'; export const DOWNLOAD_COMPLETE = 'GEONODE_DOWNLOAD_COMPLETE'; export const UPDATE_SINGLE_RESOURCE = 'GEONODE_UPDATE_SINGLE_RESOURCE'; +export const SET_FAVORITE_RESOURCES = 'GEONODE_SET_FAVORITE_RESOURCES'; +export const REMOVE_FAVORITE_RESOURCE = 'GEONODE_REMOVE_FAVORITE_RESOURCE'; /** @@ -309,3 +311,17 @@ export function downloadComplete(resource) { resource }; } + +export function setFavoriteResources(favorites) { + return { + type: SET_FAVORITE_RESOURCES, + favorites + }; +} + +export function removeFavoriteResource(pk) { + return { + type: REMOVE_FAVORITE_RESOURCE, + pk + }; +} diff --git a/geonode_mapstore_client/client/js/api/geonode/v2/index.js b/geonode_mapstore_client/client/js/api/geonode/v2/index.js index 0868e2e1c3..80a14133f6 100644 --- a/geonode_mapstore_client/client/js/api/geonode/v2/index.js +++ b/geonode_mapstore_client/client/js/api/geonode/v2/index.js @@ -297,6 +297,11 @@ export const setFavoriteResource = (pk, favorite) => { .then(({ data }) => data ); }; +export const getUserFavoriteResources = () => { + return axios.get(parseDevHostname(`${endpoints[RESOURCES]}/favorites`)) + .then(({ data }) => data.favorites.map(({ pk }) => pk)); +}; + export const getResourceByPk = (pk) => { return axios.get(parseDevHostname(`${endpoints[RESOURCES]}/${pk}`), { params: { diff --git a/geonode_mapstore_client/client/js/components/ActionNavbar/ActionNavbar.jsx b/geonode_mapstore_client/client/js/components/ActionNavbar/ActionNavbar.jsx index 34d2217c04..6f9ba100d5 100644 --- a/geonode_mapstore_client/client/js/components/ActionNavbar/ActionNavbar.jsx +++ b/geonode_mapstore_client/client/js/components/ActionNavbar/ActionNavbar.jsx @@ -100,7 +100,7 @@ const ActionNavbar = forwardRef( query={query} variant={variant} size={size} - resourceName={resource.title} + resourceName={resource?.title} /> )} {rightItems.length > 0 && ( diff --git a/geonode_mapstore_client/client/js/components/DetailsPanel/DetailsPanel.jsx b/geonode_mapstore_client/client/js/components/DetailsPanel/DetailsPanel.jsx index 539dfccfc2..98f45ff646 100644 --- a/geonode_mapstore_client/client/js/components/DetailsPanel/DetailsPanel.jsx +++ b/geonode_mapstore_client/client/js/components/DetailsPanel/DetailsPanel.jsx @@ -29,6 +29,7 @@ import Loader from '@mapstore/framework/components/misc/Loader'; import { getUserName } from '@js/utils/SearchUtils'; import ZoomTo from '@js/components/ZoomTo'; import { boundsToExtentString } from '@js/utils/CoordinatesUtils'; +import { getUserFavoriteResources } from '@js/api/geonode/v2'; const Map = mapTypeHOC(BaseMap); Map.displayName = 'Map'; @@ -205,7 +206,10 @@ function DetailsPanel({ enableMapViewer, onClose, onAction, - canDownload + canDownload, + setFavorites, + removeFavorite, + resourceId }) { const detailsContainerNode = useRef(); const isMounted = useRef(); @@ -219,6 +223,10 @@ function DetailsPanel({ }; }, []); + useEffect(() => { + getUserFavoriteResources().then(favorites => setFavorites(favorites)); + }, [resourceId]); + if (!resource && !loading) { return null; } @@ -233,6 +241,7 @@ function DetailsPanel({ }; const handleFavorite = () => { + favorite ? removeFavorite(resourceId) : setFavorites(resourceId); onFavorite(!favorite); }; diff --git a/geonode_mapstore_client/client/js/epics/favorite.js b/geonode_mapstore_client/client/js/epics/favorite.js index cfa7119ffc..4526c68126 100644 --- a/geonode_mapstore_client/client/js/epics/favorite.js +++ b/geonode_mapstore_client/client/js/epics/favorite.js @@ -8,9 +8,10 @@ import { Observable } from 'rxjs'; import { - resourceError, updateResourceProperties, - SET_FAVORITE_RESOURCE + SET_FAVORITE_RESOURCE, + removeFavoriteResource, + setFavoriteResources } from '@js/actions/gnresource'; import { updateResources @@ -18,6 +19,9 @@ import { import { setFavoriteResource } from '@js/api/geonode/v2'; +import { + error as errorNotification +} from '@mapstore/framework/actions/notifications'; export const gnSaveFavoriteContent = (action$, store) => action$.ofType(SET_FAVORITE_RESOURCE) @@ -44,7 +48,9 @@ export const gnSaveFavoriteContent = (action$, store) => ); }) .catch((error) => { - return Observable.of(resourceError(error.data || error.message)); + return Observable.of( + action.favorite ? removeFavoriteResource(pk) : setFavoriteResources(pk), + errorNotification({ title: "gnviewer.cannotPerfomAction", message: error?.data?.message || error?.data?.detail || error?.originalError?.message || "gnviewer.syncErrorDefault" })); }); }); diff --git a/geonode_mapstore_client/client/js/plugins/DetailViewer.jsx b/geonode_mapstore_client/client/js/plugins/DetailViewer.jsx index 822d3519aa..ff0b7f3574 100644 --- a/geonode_mapstore_client/client/js/plugins/DetailViewer.jsx +++ b/geonode_mapstore_client/client/js/plugins/DetailViewer.jsx @@ -20,7 +20,9 @@ import { setMapThumbnail, setResourceThumbnail, enableMapThumbnailViewer, - downloadResource + downloadResource, + setFavoriteResources, + removeFavoriteResource } from '@js/actions/gnresource'; import { processingDownload } from '@js/selectors/resourceservice'; import FaIcon from '@js/components/FaIcon/FaIcon'; @@ -55,19 +57,21 @@ const ConnectedDetailsPanel = connect( updatingThumbnailResource, mapSelector, state => state?.gnresource?.showMapThumbnail || false, - processingDownload - ], (resource, loading, favorite, savingThumbnailMap, layers, thumbnailChanged, resourceThumbnailUpdating, mapData, showMapThumbnail, downloading) => ({ + processingDownload, + state => state?.gnresource?.favoriteResources || [] + ], (resource, loading, favorite, savingThumbnailMap, layers, thumbnailChanged, resourceThumbnailUpdating, mapData, showMapThumbnail, downloading, favorites) => ({ layers: layers, resource, loading, savingThumbnailMap, - favorite, + favorite: favorite || favorites.includes(resource?.pk), isThumbnailChanged: thumbnailChanged, resourceThumbnailUpdating, initialBbox: mapData?.bbox, enableMapViewer: showMapThumbnail, downloading, - canDownload: resourceHasPermission(resource, 'download_resourcebase') + canDownload: resourceHasPermission(resource, 'download_resourcebase'), + resourceId: resource.pk })), { closePanel: setControlProperty.bind(null, 'rightOverlay', 'enabled', false), @@ -75,7 +79,9 @@ const ConnectedDetailsPanel = connect( onMapThumbnail: setMapThumbnail, onResourceThumbnail: setResourceThumbnail, onClose: enableMapThumbnailViewer, - onAction: downloadResource + onAction: downloadResource, + setFavorites: setFavoriteResources, + removeFavorite: removeFavoriteResource } )(DetailsPanel); diff --git a/geonode_mapstore_client/client/js/reducers/__tests__/gnresource-test.js b/geonode_mapstore_client/client/js/reducers/__tests__/gnresource-test.js index 808103f18f..417ca666c2 100644 --- a/geonode_mapstore_client/client/js/reducers/__tests__/gnresource-test.js +++ b/geonode_mapstore_client/client/js/reducers/__tests__/gnresource-test.js @@ -87,6 +87,7 @@ describe('gnresource reducer', () => { selectedLayerPermissions: [], data: {}, permissions: [], + favoriteResources: [], isNew: true }); }); diff --git a/geonode_mapstore_client/client/js/reducers/gnresource.js b/geonode_mapstore_client/client/js/reducers/gnresource.js index 6d881fe6af..0551f7f0b7 100644 --- a/geonode_mapstore_client/client/js/reducers/gnresource.js +++ b/geonode_mapstore_client/client/js/reducers/gnresource.js @@ -6,7 +6,7 @@ * LICENSE file in the root directory of this source tree. */ -import isEqual from 'lodash/isEqual'; +import {isEqual, uniq} from 'lodash'; import { RESOURCE_LOADING, SET_RESOURCE, @@ -27,7 +27,9 @@ import { SET_RESOURCE_COMPACT_PERMISSIONS, UPDATE_RESOURCE_COMPACT_PERMISSIONS, RESET_GEO_LIMITS, - ENABLE_MAP_THUMBNAIL_VIEWER + ENABLE_MAP_THUMBNAIL_VIEWER, + SET_FAVORITE_RESOURCES, + REMOVE_FAVORITE_RESOURCE } from '@js/actions/gnresource'; import { @@ -38,7 +40,8 @@ import { const defaultState = { selectedLayerPermissions: [], data: {}, - permissions: [] + permissions: [], + favoriteResources: [] }; function gnresource(state = defaultState, action) { @@ -208,6 +211,20 @@ function gnresource(state = defaultState, action) { }; } return state; + case SET_FAVORITE_RESOURCES: { + const favoriteResources = Array.isArray(action.favorites) ? action.favorites : uniq([...state.favoriteResources, action.favorites]); + return { + ...state, + favoriteResources: [...favoriteResources] + }; + } + case REMOVE_FAVORITE_RESOURCE: { + const newFavorites = state.favoriteResources?.filter(fav => fav !== action.pk); + return { + ...state, + favoriteResources: [...newFavorites] + }; + } default: return state; } diff --git a/geonode_mapstore_client/client/js/routes/Detail.jsx b/geonode_mapstore_client/client/js/routes/Detail.jsx index ca1c718deb..38226b02d2 100644 --- a/geonode_mapstore_client/client/js/routes/Detail.jsx +++ b/geonode_mapstore_client/client/js/routes/Detail.jsx @@ -24,7 +24,7 @@ import { loadFeaturedResources } from '@js/actions/gnsearch'; -import { downloadResource, setFavoriteResource } from '@js/actions/gnresource'; +import { downloadResource, setFavoriteResource, setFavoriteResources, removeFavoriteResource } from '@js/actions/gnresource'; import { hashLocationToHref, clearQueryParams, @@ -51,16 +51,20 @@ const ConnectedDetailsPanel = connect( state => state?.gnresource?.loading || false, state => state?.gnresource?.data?.favorite || false, processingDownload, - state => state?.gnresource?.data || null - ], (loading, favorite, downloading, resource) => ({ + state => state?.gnresource?.data || null, + state => state?.gnresource?.favoriteResources || [] + ], (loading, favorite, downloading, resource, favorites) => ({ loading, - favorite, + favorite: favorite || favorites.includes(resource?.pk), downloading, - canDownload: resourceHasPermission(resource, 'download_resourcebase') + canDownload: resourceHasPermission(resource, 'download_resourcebase'), + resourceId: resource.pk })), { onFavorite: setFavoriteResource, - onAction: downloadResource + onAction: downloadResource, + setFavorites: setFavoriteResources, + removeFavorite: removeFavoriteResource } )(DetailsPanel); function Detail({