diff --git a/web/modals/threads/members/member.react.js b/web/modals/threads/members/member.react.js index 183dcf044..6cef6ffdf 100644 --- a/web/modals/threads/members/member.react.js +++ b/web/modals/threads/members/member.react.js @@ -1,138 +1,190 @@ // @flow import { faPlusCircle, faMinusCircle, faSignOutAlt, } from '@fortawesome/free-solid-svg-icons'; import classNames from 'classnames'; import * as React from 'react'; +import { + removeUsersFromThread, + changeThreadMemberRoles, +} from 'lib/actions/thread-actions'; import { memberIsAdmin, memberHasAdminPowers, threadHasPermission, + removeMemberFromThread, + switchMemberAdminRoleInThread, } from 'lib/shared/thread-utils'; import { stringForUser } from 'lib/shared/user-utils'; import type { SetState } from 'lib/types/hook-types'; import { type RelativeMemberInfo, type ThreadInfo, threadPermissions, } from 'lib/types/thread-types'; +import { + useDispatchActionPromise, + useServerCall, +} from 'lib/utils/action-utils'; import Label from '../../../components/label.react'; import MenuItem from '../../../components/menu-item.react'; import Menu from '../../../components/menu.react'; import SWMansionIcon from '../../../SWMansionIcon.react'; import css from './members-modal.css'; type Props = { +memberInfo: RelativeMemberInfo, +threadInfo: ThreadInfo, +setOpenMenu: SetState, +isMenuOpen: boolean, }; function ThreadMember(props: Props): React.Node { const { memberInfo, threadInfo, setOpenMenu, isMenuOpen } = props; const userName = stringForUser(memberInfo); const onMenuChange = React.useCallback( menuOpen => { if (menuOpen) { setOpenMenu(() => memberInfo.id); } else { setOpenMenu(menu => (menu === memberInfo.id ? null : menu)); } }, [memberInfo.id, setOpenMenu], ); + const dispatchActionPromise = useDispatchActionPromise(); + const boundRemoveUsersFromThread = useServerCall(removeUsersFromThread); + + const onClickRemoveUser = React.useCallback( + () => + removeMemberFromThread( + threadInfo, + memberInfo, + dispatchActionPromise, + boundRemoveUsersFromThread, + ), + [boundRemoveUsersFromThread, dispatchActionPromise, memberInfo, threadInfo], + ); + + const isCurrentlyAdmin = memberIsAdmin(memberInfo, threadInfo); + const boundChangeThreadMemberRoles = useServerCall(changeThreadMemberRoles); + + const onMemberAdminRoleToggled = React.useCallback( + () => + switchMemberAdminRoleInThread( + threadInfo, + memberInfo, + isCurrentlyAdmin, + dispatchActionPromise, + boundChangeThreadMemberRoles, + ), + [ + boundChangeThreadMemberRoles, + dispatchActionPromise, + isCurrentlyAdmin, + memberInfo, + threadInfo, + ], + ); + const menuItems = React.useMemo(() => { const { role } = memberInfo; if (!role) { return []; } const canRemoveMembers = threadHasPermission( threadInfo, threadPermissions.REMOVE_MEMBERS, ); const canChangeRoles = threadHasPermission( threadInfo, threadPermissions.CHANGE_ROLE, ); const actions = []; const isAdmin = memberIsAdmin(memberInfo, threadInfo); if (canChangeRoles && memberInfo.username && isAdmin) { actions.push( , ); } else if (canChangeRoles && memberInfo.username) { actions.push( - , + , ); } if ( canRemoveMembers && !memberInfo.isViewer && (canChangeRoles || threadInfo.roles[role]?.isDefault) ) { actions.push( , ); } return actions; - }, [memberInfo, threadInfo]); + }, [memberInfo, onClickRemoveUser, onMemberAdminRoleToggled, threadInfo]); const userSettingsIcon = React.useMemo( () => , [], ); const label = React.useMemo(() => { if (memberIsAdmin(memberInfo, threadInfo)) { return ; } else if (memberHasAdminPowers(memberInfo)) { return ; } return null; }, [memberInfo, threadInfo]); const memberContainerClasses = classNames(css.memberContainer, { [css.memberContainerWithMenuOpen]: isMenuOpen, }); return (
{userName} {label}
{menuItems}
); } export default ThreadMember;