import React, { useCallback, useState } from 'react'; import { PageGrant, isPopulated, GroupType, type IGrantedGroup, } from '@growi/core'; import { LoadingSpinner } from '@growi/ui/dist/components'; import { useTranslation } from 'next-i18next'; import { UncontrolledDropdown, DropdownToggle, DropdownMenu, DropdownItem, Modal, ModalHeader, ModalBody, } from 'reactstrap'; import type { IPageGrantData } from '~/interfaces/page'; import { useCurrentUser } from '~/stores/context'; import { useMyUserGroups } from './use-my-user-groups'; const AVAILABLE_GRANTS = [ { grant: PageGrant.GRANT_PUBLIC, iconName: 'group', btnStyleClass: 'outline-info', label: 'Public', }, { grant: PageGrant.GRANT_RESTRICTED, iconName: 'link', btnStyleClass: 'outline-success', label: 'Anyone with the link', }, // { grant: 3, iconClass: '', label: 'Specified users only' }, { grant: PageGrant.GRANT_OWNER, iconName: 'lock', btnStyleClass: 'outline-danger', label: 'Only me', }, { grant: PageGrant.GRANT_USER_GROUP, iconName: 'more_horiz', btnStyleClass: 'outline-warning', label: 'Only inside the group', reselectLabel: 'Reselect the group', }, ]; type Props = { disabled?: boolean, openInModal?: boolean, grant: PageGrant, userRelatedGrantedGroups?: { id: string, name: string, type: GroupType, }[] onUpdateGrant?: (grantData: IPageGrantData) => void, } /** * Page grant select component */ export const GrantSelector = (props: Props): JSX.Element => { const { t } = useTranslation(); const { disabled, openInModal, userRelatedGrantedGroups, onUpdateGrant, grant: currentGrant, } = props; const [isSelectGroupModalShown, setIsSelectGroupModalShown] = useState(false); const { data: currentUser } = useCurrentUser(); const shouldFetch = isSelectGroupModalShown; const { data: myUserGroups, update: updateMyUserGroups } = useMyUserGroups(shouldFetch); const showSelectGroupModal = useCallback(() => { updateMyUserGroups(); setIsSelectGroupModalShown(true); }, [updateMyUserGroups]); /** * change event handler for grant selector */ const changeGrantHandler = useCallback((grant: PageGrant) => { // select group if (grant === 5) { showSelectGroupModal(); return; } if (onUpdateGrant != null) { onUpdateGrant({ grant, userRelatedGrantedGroups: undefined }); } }, [onUpdateGrant, showSelectGroupModal]); const groupListItemClickHandler = useCallback((grantGroup: IGrantedGroup) => { if (onUpdateGrant != null && isPopulated(grantGroup.item)) { let userRelatedGrantedGroupsCopy = userRelatedGrantedGroups != null ? [...userRelatedGrantedGroups] : []; const grantGroupInfo = { id: grantGroup.item._id, name: grantGroup.item.name, type: grantGroup.type }; if (userRelatedGrantedGroupsCopy.find(group => group.id === grantGroupInfo.id) == null) { userRelatedGrantedGroupsCopy.push(grantGroupInfo); } else { userRelatedGrantedGroupsCopy = userRelatedGrantedGroupsCopy.filter(group => group.id !== grantGroupInfo.id); } onUpdateGrant({ grant: 5, userRelatedGrantedGroups: userRelatedGrantedGroupsCopy }); } }, [onUpdateGrant, userRelatedGrantedGroups]); /** * Render grant selector DOM. */ const renderGrantSelector = useCallback(() => { let dropdownToggleBtnColor; let dropdownToggleLabelElm; const dropdownMenuElems = AVAILABLE_GRANTS.map((opt) => { const label = ((opt.grant === 5 && opt.reselectLabel != null) && userRelatedGrantedGroups != null && userRelatedGrantedGroups.length > 0) ? opt.reselectLabel // when grantGroup is selected : opt.label; const labelElm = ( {opt.iconName} {t(label)} ); // set dropdownToggleBtnColor, dropdownToggleLabelElm if (opt.grant === 1 || opt.grant === currentGrant) { dropdownToggleBtnColor = opt.btnStyleClass; dropdownToggleLabelElm = labelElm; } return changeGrantHandler(opt.grant)}>{labelElm}; }); // add specified group option if (userRelatedGrantedGroups != null && userRelatedGrantedGroups.length > 0) { const labelElm = ( account_tree {userRelatedGrantedGroups.length > 1 ? ( // substring for group name truncate {`${userRelatedGrantedGroups[0].name.substring(0, 30)}, ... `} +{userRelatedGrantedGroups.length - 1} ) : userRelatedGrantedGroups[0].name.substring(0, 30)} ); // set dropdownToggleLabelElm dropdownToggleLabelElm = labelElm; dropdownMenuElems.push({labelElm}); } return (
{dropdownToggleLabelElm} {dropdownMenuElems}
); }, [changeGrantHandler, currentGrant, disabled, userRelatedGrantedGroups, t, openInModal]); /** * Render select grantgroup modal. */ const renderSelectGroupModalContent = useCallback(() => { if (!shouldFetch) { return <>; } // show spinner if (myUserGroups == null) { return (
); } if (myUserGroups.length === 0) { return (

{t('user_group.belonging_to_no_group')}

{ currentUser?.admin && (

login{t('user_group.manage_user_groups')}

) }
); } return (
{ myUserGroups.map((group) => { const groupIsGranted = userRelatedGrantedGroups?.find(g => g.id === group.item._id) != null; const activeClass = groupIsGranted ? 'active' : ''; return ( ); }) }
); }, [currentUser?.admin, groupListItemClickHandler, myUserGroups, shouldFetch, t, userRelatedGrantedGroups]); const renderModalCloseButton = useCallback(() => { return ( ); }, [setIsSelectGroupModalShown]); return ( <> { renderGrantSelector() } {/* render modal */} { !disabled && currentUser != null && ( setIsSelectGroupModalShown(false)} centered > setIsSelectGroupModalShown(false)} className="fs-5 text-muted fw-bold pb-2" close={renderModalCloseButton()}> {t('user_group.select_group')} {renderSelectGroupModalContent()} ) } ); };