From 705b5cc1072bfb178195a90091e6d3dc120d193d Mon Sep 17 00:00:00 2001 From: Stephen Smith Date: Sun, 28 Nov 2021 20:28:41 -0500 Subject: [PATCH] 18123: Add active toggle to group member list. Arvados-DCO-1.1-Signed-off-by: Stephen Smith --- .../group-details-panel-actions.ts | 45 +++++++++++++++++++ ...etails-panel-members-middleware-service.ts | 3 ++ src/store/resources/resources-actions.ts | 2 + .../data-explorer/renderers.tsx | 39 ++++++++++++++-- .../group-details-panel.tsx | 10 ++++- 5 files changed, 95 insertions(+), 4 deletions(-) diff --git a/src/store/group-details-panel/group-details-panel-actions.ts b/src/store/group-details-panel/group-details-panel-actions.ts index 6f594de0..43349fa9 100644 --- a/src/store/group-details-panel/group-details-panel-actions.ts +++ b/src/store/group-details-panel/group-details-panel-actions.ts @@ -18,6 +18,8 @@ import { ServiceRepository } from 'services/services'; import { PermissionResource, PermissionLevel } from 'models/permission'; import { snackbarActions, SnackbarKind } from 'store/snackbar/snackbar-actions'; import { PermissionSelectValue, parsePermissionLevel, formatPermissionLevel } from 'views-components/sharing-dialog/permission-select'; +import { LinkResource } from 'models/link'; +import { deleteResources } from 'store/resources/resources-actions'; export const GROUP_DETAILS_MEMBERS_PANEL_ID = 'groupDetailsMembersPanel'; export const GROUP_DETAILS_PERMISSIONS_PANEL_ID = 'groupDetailsPermissionsPanel'; @@ -162,3 +164,46 @@ export const removeGroupMember = (uuid: string) => } }; + +export const setMemberIsHidden = (memberLinkUuid: string, permissionLinkUuid: string, hide: boolean) => + async (dispatch: Dispatch, getState: () => RootState, { permissionService }: ServiceRepository) => { + const memberLink = getResource(memberLinkUuid)(getState().resources); + + if (hide && permissionLinkUuid) { + // Remove read permission + try { + await permissionService.delete(permissionLinkUuid); + dispatch(deleteResources([permissionLinkUuid])); + dispatch(snackbarActions.OPEN_SNACKBAR({ + message: 'Removed read permission.', + hideDuration: 2000, + kind: SnackbarKind.SUCCESS, + })); + } catch (e) { + dispatch(snackbarActions.OPEN_SNACKBAR({ + message: 'Failed to remove permission', + kind: SnackbarKind.ERROR, + })); + } + } else if (!hide && memberLink) { + // Create read permission + try { + await permissionService.create({ + headUuid: memberLink.tailUuid, + tailUuid: memberLink.headUuid, + name: PermissionLevel.CAN_READ, + }); + dispatch(snackbarActions.OPEN_SNACKBAR({ + message: 'Created read permission.', + hideDuration: 2000, + kind: SnackbarKind.SUCCESS, + })); + dispatch(GroupPermissionsPanelActions.REQUEST_ITEMS()); + } catch(e) { + dispatch(snackbarActions.OPEN_SNACKBAR({ + message: 'Failed to create permission', + kind: SnackbarKind.ERROR, + })); + } + } + }; diff --git a/src/store/group-details-panel/group-details-panel-members-middleware-service.ts b/src/store/group-details-panel/group-details-panel-members-middleware-service.ts index 36e3c735..e6f18f7f 100644 --- a/src/store/group-details-panel/group-details-panel-members-middleware-service.ts +++ b/src/store/group-details-panel/group-details-panel-members-middleware-service.ts @@ -27,6 +27,9 @@ export class GroupDetailsPanelMembersMiddlewareService extends DataExplorerMiddl api.dispatch(groupsDetailsPanelDataExplorerIsNotSet()); } else { try { + const groupResource = await this.services.groupsService.get(groupUuid); + api.dispatch(updateResources([groupResource])); + const permissionsIn = await this.services.permissionService.list({ filters: new FilterBuilder() .addEqual('head_uuid', groupUuid) diff --git a/src/store/resources/resources-actions.ts b/src/store/resources/resources-actions.ts index cdb76e0b..6c05da32 100644 --- a/src/store/resources/resources-actions.ts +++ b/src/store/resources/resources-actions.ts @@ -18,6 +18,8 @@ export type ResourcesAction = UnionOf; export const updateResources = (resources: Resource[]) => resourcesActions.SET_RESOURCES(resources); +export const deleteResources = (resources: string[]) => resourcesActions.DELETE_RESOURCES(resources); + export const loadResource = (uuid: string, showErrors?: boolean) => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { try { diff --git a/src/views-components/data-explorer/renderers.tsx b/src/views-components/data-explorer/renderers.tsx index 61086bd8..44326a85 100644 --- a/src/views-components/data-explorer/renderers.tsx +++ b/src/views-components/data-explorer/renderers.tsx @@ -11,7 +11,7 @@ import { formatDate, formatFileSize, formatTime } from 'common/formatters'; import { resourceLabel } from 'common/labels'; import { connect, DispatchProp } from 'react-redux'; import { RootState } from 'store/store'; -import { getResource } from 'store/resources/resources'; +import { getResource, filterResources } from 'store/resources/resources'; import { GroupContentsResource } from 'services/groups-service/groups-service'; import { getProcess, Process, getProcessStatus, getProcessStatusColor, getProcessRuntime } from 'store/processes/process'; import { ArvadosTheme } from 'common/custom-theme'; @@ -22,14 +22,15 @@ import { getUuidPrefix, openRunProcess } from 'store/workflow-panel/workflow-pan import { openSharingDialog } from 'store/sharing-dialog/sharing-dialog-actions'; import { getUserFullname, getUserDisplayName, User, UserResource } from 'models/user'; import { toggleIsActive, toggleIsAdmin } from 'store/users/users-actions'; -import { LinkResource } from 'models/link'; +import { LinkClass, LinkResource } from 'models/link'; import { navigateTo, navigateToGroupDetails } from 'store/navigation/navigation-action'; import { withResourceData } from 'views-components/data-explorer/with-resources'; import { CollectionResource } from 'models/collection'; import { IllegalNamingWarning } from 'components/warning/warning'; import { loadResource } from 'store/resources/resources-actions'; -import { GroupClass } from 'models/group'; +import { GroupClass, GroupResource } from 'models/group'; import { openRemoveGroupMemberDialog, openEditPermissionLevelDialog } from 'store/group-details-panel/group-details-panel-actions'; +import { setMemberIsHidden } from 'store/group-details-panel/group-details-panel-actions'; import { formatPermissionLevel } from 'views-components/sharing-dialog/permission-select'; import { PermissionLevel } from 'models/permission'; @@ -212,6 +213,38 @@ export const ResourceLinkTailIsActive = connect( }, { toggleIsActive } )(renderIsActive); +const renderIsHidden = (props: { memberLinkUuid: string, permissionLinkUuid: string, hidden: boolean, setMemberIsHidden: (memberLinkUuid: string, permissionLinkUuid: string, hide: boolean) => void }) => { + if (props.memberLinkUuid) { + return props.setMemberIsHidden(props.memberLinkUuid, props.permissionLinkUuid, !props.hidden)} />; + } else { + return ; + } +} + +export const ResourceLinkTailIsHidden = connect( + (state: RootState, props: { uuid: string }) => { + const link = getResource(props.uuid)(state.resources); + const member = getResource(link?.tailUuid || '')(state.resources); + const group = getResource(link?.headUuid || '')(state.resources); + const permissions = filterResources((resource: LinkResource) => { + return resource.linkClass === LinkClass.PERMISSION + && resource.headUuid === link?.tailUuid + && resource.tailUuid === group?.uuid + && resource.name === PermissionLevel.CAN_READ; + })(state.resources); + + const permissionLinkUuid = permissions.length > 0 ? permissions[0].uuid : ''; + const isVisible = link && group && permissions.length > 0; + + return member?.kind === ResourceKind.USER + ? { memberLinkUuid: link?.uuid, permissionLinkUuid, hidden: !isVisible } + : { memberLinkUuid: '', permissionLinkUuid: '', hidden: false}; + }, { setMemberIsHidden } +)(renderIsHidden); + const renderIsAdmin = (props: { uuid: string, isAdmin: boolean, toggleIsAdmin: (uuid: string) => void }) =>