@craftercms/studio-ui
Version:
Services, components, models & utils to build CrafterCMS authoring extensions.
193 lines (191 loc) • 7.25 kB
JavaScript
/*
* Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { nnou } from '../../utils/object';
import { addUserToGroup, deleteUserFromGroup, fetchAll, fetchUsersFromGroup } from '../../services/groups';
import { map, switchMap } from 'rxjs/operators';
import { forkJoin } from 'rxjs';
import TransferListColumn from '../TransferListColumn/TransferListColumn';
import { showSystemNotification } from '../../state/actions/system';
import { filterTransferListItemsByKeyword } from '../TransferList/utils';
const messages = defineMessages({
addToGroupsSuccess: {
id: 'userGroupMembershipEditor.addToGroupsSuccess',
defaultMessage: '"{user}" added to {numOfGroups, plural, one {the specified group} other {{numOfGroups} groups}}'
},
removeFromGroupsSuccess: {
id: 'userGroupMembershipEditor.removeFromGroupsSuccess',
defaultMessage:
'"{user}" removed from {numOfGroups, plural, one {the specified group} other {{numOfGroups} groups}}'
},
error: {
id: 'userGroupMembershipEditor.addOrRemoveError',
defaultMessage: 'Error modifying user group(s). Please try again momentarily.'
}
});
export function UserGroupMembershipEditor(props) {
const { username, onChange } = props;
const dispatch = useDispatch();
const { formatMessage } = useIntl();
const [groups, setGroups] = useState([]);
const [selectedGroups, setSelectedGroups] = useState({});
const [groupsFilterKeyword, setGroupsFilterKeyword] = useState('');
const [inProgressIds, setInProgressIds] = useState([]);
const refs = useRef({ inProgressIds });
const transferListItems = useMemo(
() =>
filterTransferListItemsByKeyword(
groups.map((group) => ({
id: `${group.id}`,
title: group.name,
subtitle: group.desc
})),
groupsFilterKeyword
),
[groups, groupsFilterKeyword]
);
refs.current.inProgressIds = inProgressIds;
useEffect(() => {
if (nnou(username)) {
fetchAll({ limit: 100 })
.pipe(
switchMap((groups) =>
forkJoin(groups.map((group) => fetchUsersFromGroup(group.id))).pipe(
map((membersOfEachGroup) => {
const groupsUserIsIn = membersOfEachGroup.reduce((accum, users, index) => {
if (Boolean(users.find((user) => user.username === username))) {
accum[groups[index].id] = true;
}
return accum;
}, {});
return [groups, groupsUserIsIn];
})
)
)
)
.subscribe(([groups, groupsUserIsIn]) => {
setGroups(groups);
setSelectedGroups(groupsUserIsIn);
});
} else {
fetchAll({ limit: 100 }).subscribe(setGroups);
setSelectedGroups({});
}
}, [username]);
const addRemoveFn = (groups, op) => {
const ids = groups.map((group) => group.id);
const service = op === 'add' ? addUserToGroup : deleteUserFromGroup;
setInProgressIds([...inProgressIds, ...ids]);
forkJoin(groups.map((group) => service(Number(group.id), username))).subscribe(
(results) => {
setInProgressIds(refs.current.inProgressIds.filter((id) => !ids.includes(id)));
dispatch(
showSystemNotification({
message: formatMessage(messages[op === 'add' ? 'addToGroupsSuccess' : 'removeFromGroupsSuccess'], {
user: username,
numOfGroups: groups.length
}),
options: { variant: 'success' }
})
);
},
() => {
setInProgressIds(refs.current.inProgressIds.filter((id) => !ids.includes(id)));
const next = {};
for (let id in selectedGroups) {
if (selectedGroups.hasOwnProperty(id) && !ids.includes(id)) {
next[id] = true;
}
}
setSelectedGroups(next);
dispatch(
showSystemNotification({
message: formatMessage(messages.error),
options: { variant: 'error' }
})
);
}
);
};
const addTo = (groups) => {
addRemoveFn(groups, 'add');
};
const removeFrom = (groups) => {
addRemoveFn(groups, 'remove');
};
const onItemClick = (group) => {
const next = {};
const including = !selectedGroups[group.id];
if (including) {
next[group.id] = true;
}
for (let id in selectedGroups) {
if (selectedGroups.hasOwnProperty(id) && selectedGroups[id] && (including || id !== group.id)) {
next[id] = true;
}
}
if (username) {
if (next[group.id]) {
addTo([group]);
} else {
removeFrom([group]);
}
}
setSelectedGroups(next);
onChange === null || onChange === void 0 ? void 0 : onChange(Object.keys(next));
};
const onCheckAllClicked = (items, checked) => {
const next = {};
if (checked) {
transferListItems.forEach((group) => (next[group.id] = true));
}
setSelectedGroups(next);
onChange === null || onChange === void 0 ? void 0 : onChange(Object.keys(next));
};
return React.createElement(TransferListColumn, {
title: React.createElement(FormattedMessage, { id: 'words.groups', defaultMessage: 'Groups' }),
items: transferListItems,
onItemClick: onItemClick,
checkedList: selectedGroups,
inProgressIds: inProgressIds,
isAllChecked: username
? null
: !(transferListItems === null || transferListItems === void 0
? void 0
: transferListItems.some((group) => selectedGroups[group.id] !== true)),
onCheckAllClicked: username ? null : onCheckAllClicked,
filterKeyword: groupsFilterKeyword,
setFilterKeyword: setGroupsFilterKeyword
});
}
export default UserGroupMembershipEditor;