Skip to content

Commit

Permalink
introduces async processes for cards actions (delete) (#412)
Browse files Browse the repository at this point in the history
  • Loading branch information
allyoucanmap authored Aug 11, 2021
1 parent 7d4936d commit 3f81799
Show file tree
Hide file tree
Showing 34 changed files with 850 additions and 182 deletions.
10 changes: 10 additions & 0 deletions geonode_mapstore_client/client/js/actions/gnresource.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export const RESOURCE_CONFIG_ERROR = 'GEONODE:RESOURCE_CONFIG_ERROR';
export const SET_RESOURCE_COMPACT_PERMISSIONS = 'GEONODE:SET_RESOURCE_COMPACT_PERMISSIONS';
export const UPDATE_RESOURCE_COMPACT_PERMISSIONS = 'GEONODE:UPDATE_RESOURCE_COMPACT_PERMISSIONS';
export const RESET_GEO_LIMITS = 'GEONODE:RESET_GEO_LIMITS';
export const PROCESS_RESOURCES = 'GEONODE:PROCESS_RESOURCES';

/**
* Actions for GeoNode resource
Expand Down Expand Up @@ -250,3 +251,12 @@ export function resetGeoLimits() {
type: RESET_GEO_LIMITS
};
}

export function processResources(processType, resources, redirectTo) {
return {
type: PROCESS_RESOURCES,
processType,
resources,
redirectTo
};
}
32 changes: 32 additions & 0 deletions geonode_mapstore_client/client/js/actions/resourceservice.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright 2021, 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.
*/

export const START_ASYNC_PROCESS = 'GEONODE:START_ASYNC_PROCESS';
export const STOP_ASYNC_PROCESS = 'GEONODE:STOP_ASYNC_PROCESS';
export const UPDATE_ASYNC_PROCESS = 'GEONODE:UPDATE_ASYNC_PROCESS';

export function startAsyncProcess(payload) {
return {
type: START_ASYNC_PROCESS,
payload
};
}

export function updateAsyncProcess(payload) {
return {
type: UPDATE_ASYNC_PROCESS,
payload
};
}

export function stopAsyncProcess(payload) {
return {
type: STOP_ASYNC_PROCESS,
payload
};
}
14 changes: 13 additions & 1 deletion geonode_mapstore_client/client/js/api/geonode/v2/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,16 @@ export const updateCompactPermissionsByPk = (pk, body) => {
.then(({ data }) => data);
};

export const deleteResource = (resource) => {
return axios.delete(parseDevHostname(`${endpoints[RESOURCES]}/${resource.pk}/delete`))
.then(({ data }) => data);
};

export const copyResource = (resource) => {
return axios.put(parseDevHostname(`${endpoints[RESOURCES]}/${resource.pk}/copy`))
.then(({ data }) => data);
};

export default {
getEndpoints,
getResources,
Expand All @@ -703,5 +713,7 @@ export default {
getOwners,
getKeywords,
getCompactPermissionsByPk,
updateCompactPermissionsByPk
updateCompactPermissionsByPk,
deleteResource,
copyResource
};
4 changes: 4 additions & 0 deletions geonode_mapstore_client/client/js/apps/gn-catalogue.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import ViewerRoute from '@js/routes/Viewer';

import gnsearch from '@js/reducers/gnsearch';
import gnresource from '@js/reducers/gnresource';
import resourceservice from '@js/reducers/resourceservice';
import gnsettings from '@js/reducers/gnsettings';

import {
Expand All @@ -67,6 +68,7 @@ import {
} from '@js/epics';

import gnresourceEpics from '@js/epics/gnresource';
import resourceServiceEpics from '@js/epics/resourceservice';
import gnsearchEpics from '@js/epics/gnsearch';
import favoriteEpics from '@js/epics/favorite';
import maplayout from '@mapstore/framework/reducers/maplayout';
Expand Down Expand Up @@ -251,6 +253,7 @@ Promise.all([
appReducers: {
...standardReducers,
gnresource,
resourceservice,
gnsettings,
security,
maptype,
Expand All @@ -276,6 +279,7 @@ Promise.all([
gnSetDatasetsPermissions,
...pluginsDefinition.epics,
...gnresourceEpics,
...resourceServiceEpics,
...gnsearchEpics,
...favoriteEpics,
updateMapLayoutEpic
Expand Down
7 changes: 5 additions & 2 deletions geonode_mapstore_client/client/js/apps/gn-home.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@ import { connect } from 'react-redux';

import security from '@mapstore/framework/reducers/security';
import controls from '@mapstore/framework/reducers/controls';

import Home from '@js/routes/Home';

import gnsearch from '@js/reducers/gnsearch';
import gnresource from '@js/reducers/gnresource';
import resourceservice from '@js/reducers/resourceservice';
import gnsearchEpics from '@js/epics/gnsearch';
import gnsaveEpics from '@js/epics/gnsave';
import resourceServiceEpics from '@js/epics/resourceservice';

import {
getConfiguration,
Expand Down Expand Up @@ -78,12 +79,14 @@ Promise.all([
appReducers: {
gnsearch,
gnresource,
resourceservice,
security,
controls
},
appEpics: {
...gnsearchEpics,
...gnsaveEpics
...gnsaveEpics,
...resourceServiceEpics
},
geoNodeConfiguration
});
Expand Down
38 changes: 34 additions & 4 deletions geonode_mapstore_client/client/js/components/CardGrid/CardGrid.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ import { withResizeDetector } from 'react-resize-detector';
import useLocalStorage from '@js/hooks/useLocalStorage';
import { hasPermissionsTo } from '@js/utils/MenuUtils';
import useInfiniteScroll from '@js/hooks/useInfiniteScroll';
import {
ProcessTypes,
ProcessStatus
} from '@js/utils/ResourceServiceUtils';

const Cards = withResizeDetector(({
resources,
Expand All @@ -23,7 +27,9 @@ const Cards = withResizeDetector(({
containerWidth,
width: detectedWidth,
buildHrefByTemplate,
options
options,
actions,
onAction
}) => {

const width = containerWidth || detectedWidth;
Expand Down Expand Up @@ -86,7 +92,15 @@ const Cards = withResizeDetector(({
>
{resources.map((resource, idx) => {
// enable allowedOptions (menu cards) only for list layout
const allowedOptions = (cardLayoutStyle === 'list') ? options
const { processes, ...data } = resource;
const isProcessing = processes
? !!processes.find(({ completed }) => !completed)
: false;
const deleteProcess = processes && processes.find(({ processType }) => processType === ProcessTypes.DELETE_RESOURCE);
const isDeleting = !!deleteProcess?.output?.status;
const isDeleted = deleteProcess?.output?.status === ProcessStatus.FINISHED;

const allowedOptions = (cardLayoutStyle === 'list' && !isProcessing) ? options
.filter((opt) => hasPermissionsTo(resource?.perms, opt?.perms, 'resource')) : [];

return (
Expand All @@ -95,12 +109,17 @@ const Cards = withResizeDetector(({
style={(layoutSpace(idx))}
>
<ResourceCard
className={`${isDeleted ? 'deleted' : ''}`}
active={isCardActive(resource)}
data={resource}
data={data}
formatHref={formatHref}
options={allowedOptions}
buildHrefByTemplate={buildHrefByTemplate}
layoutCardsStyle={cardLayoutStyle}
actions={actions}
onAction={onAction}
loading={isProcessing}
readOnly={isDeleted || isDeleting}
/>
</li>
);
Expand All @@ -123,7 +142,10 @@ const CardGrid = ({
messageId,
children,
buildHrefByTemplate,
scrollContainer
scrollContainer,
actions,
onAction,
onControl
}) => {

useInfiniteScroll({
Expand Down Expand Up @@ -157,6 +179,14 @@ const CardGrid = ({
isCardActive={isCardActive}
options={cardOptions}
buildHrefByTemplate={buildHrefByTemplate}
actions={actions}
onAction={(action, payload) => {
if (action.isControlled) {
onControl(action.processType, 'value', payload);
} else {
onAction(action.processType, payload, action.redirectTo);
}
}}
/>
<div className="gn-card-grid-pagination">
{loading && <Spinner animation="border" role="status">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
} from '@js/utils/ResourceUtils';
import localizedProps from '@mapstore/framework/components/misc/enhancers/localizedProps';
import { getGeoLimits } from '@js/api/geonode/security';
import Spinner from '@js/components/Spinner';

const FormControl = localizedProps('placeholder')(FormControlRB);

Expand All @@ -34,7 +35,8 @@ function Permissions({
defaultGroupOptions,
enableGeoLimits,
requestGeoLimits = getGeoLimits,
resourceId
resourceId,
loading
}) {

const { entries = [], groups = [] } = permissionsCompactToLists(compactPermissions);
Expand Down Expand Up @@ -301,6 +303,11 @@ function Permissions({
<Message msgId="gnviewer.permissionsEntriesNoResults" />
</div>
}
{loading && (
<div className="gn-spinner-container">
<Spinner />
</div>
)}
</div>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,27 @@ import React, { forwardRef } from 'react';
import Message from '@mapstore/framework/components/I18N/Message';
import FaIcon from '@js/components/FaIcon';
import Dropdown from '@js/components/Dropdown';
import Spinner from '@js/components/Spinner';
import { getUserName } from '@js/utils/SearchUtils';
import { getResourceTypesInfo } from '@js/utils/ResourceUtils';

function ALink({ href, readOnly, children }) {
return readOnly ? children : <a href={href}>{children}</a>;
}

const ResourceCard = forwardRef(({
data,
active,
options,
formatHref,
getTypesInfo,
layoutCardsStyle,
buildHrefByTemplate
buildHrefByTemplate,
readOnly,
actions,
onAction,
className,
loading
}, ref) => {

const res = data;
Expand All @@ -29,37 +40,39 @@ const ResourceCard = forwardRef(({
return (
<div
ref={ref}
className={`gn-resource-card${active ? ' active' : ''} gn-card-type-${layoutCardsStyle} ${layoutCardsStyle === 'list' ? 'rounded-0' : ''}`}
className={`gn-resource-card${active ? ' active' : ''}${readOnly ? ' read-only' : ''} gn-card-type-${layoutCardsStyle} ${layoutCardsStyle === 'list' ? 'rounded-0' : ''}${className ? ` ${className}` : ''}`}
>
<a
{!readOnly && <a
className="gn-resource-card-link"
href={formatHref({
pathname: `/detail/${res.resource_type}/${res.pk}`
})}
/>
/>}
<div className={`card-resource-${layoutCardsStyle}`}>
<img
className={`${(layoutCardsStyle === 'list') ? 'card-img-left' : 'card-img-top'}`}
src={res.thumbnail_url}
/>
<div className="card-body">
<div className="card-title">
{icon &&
{(icon && !loading) &&
<>
<a
<ALink
readOnly={readOnly}
href={formatHref({
query: {
'filter{resource_type.in}': res.resource_type
}
})}>
<FaIcon name={icon} />
</a>
</ALink>
</>}
<a href={formatHref({
{loading && <Spinner />}
<ALink readOnly={readOnly} href={formatHref({
pathname: `/detail/${res.resource_type}/${res.pk}`
})}>
{res.title}
</a>
</ALink>
</div>
<p
className="card-text gn-card-description"
Expand All @@ -69,15 +82,15 @@ const ResourceCard = forwardRef(({
<p
className="card-text gn-card-user"
>
<Message msgId="gnhome.author"/>: <a href={formatHref({
<Message msgId="gnhome.author"/>: <ALink readOnly={readOnly} href={formatHref({
query: {
'filter{owner.username.in}': res.owner.username
}
})}>{getUserName(res.owner)}</a>
})}>{getUserName(res.owner)}</ALink>
</p>

</div>
{options && options.length > 0 && <Dropdown
{(!readOnly && options && options.length > 0) && <Dropdown
className="gn-card-options"
pullRight
>
Expand All @@ -92,7 +105,16 @@ const ResourceCard = forwardRef(({
<Dropdown.Menu className={`gn-card-dropdown`} >
{options
.map((opt) => {

if (opt.type === 'button' && actions[opt.action]) {
return (
<Dropdown.Item
key={opt.action}
onClick={() => onAction(actions[opt.action], [res])}
>
<FaIcon name={opt.icon} /> <Message msgId={opt.labelId}/>
</Dropdown.Item>
);
}
const viewResourcebase = opt.perms.filter(obj => {
return obj.value === "view_resourcebase";
});
Expand All @@ -102,7 +124,7 @@ const ResourceCard = forwardRef(({
key={opt.href}
href={
(viewResourcebase.length > 0 ) ? formatHref({
pathname: `/detail/${res.resource_type}/${res.pk}`
pathname: `/${res.resource_type}/${res.pk}`
}) : buildHrefByTemplate(res, opt.href)
}
>
Expand All @@ -120,7 +142,8 @@ const ResourceCard = forwardRef(({
ResourceCard.defaultProps = {
links: [],
theme: 'light',
getTypesInfo: getResourceTypesInfo
getTypesInfo: getResourceTypesInfo,
formatHref: () => '#'
};

export default ResourceCard;
Loading

0 comments on commit 3f81799

Please sign in to comment.