diff --git a/web/modals/components/add-members-item.react.js b/web/modals/components/add-members-item.react.js index 407094031..e9d80ed70 100644 --- a/web/modals/components/add-members-item.react.js +++ b/web/modals/components/add-members-item.react.js @@ -1,51 +1,55 @@ // @flow import * as React from 'react'; import type { UserListItem } from 'lib/types/user-types.js'; import css from './add-members.css'; import Button from '../../components/button.react.js'; +import UserAvatar from '../../components/user-avatar.react.js'; type AddMembersItemProps = { +userInfo: UserListItem, +onClick: (userID: string) => void, +userAdded: boolean, }; function AddMemberItem(props: AddMembersItemProps): React.Node { const { userInfo, onClick, userAdded = false } = props; const canBeAdded = !userInfo.alertText; const onClickCallback = React.useCallback(() => { if (!canBeAdded) { return; } onClick(userInfo.id); }, [canBeAdded, onClick, userInfo.id]); const action = React.useMemo(() => { if (!canBeAdded) { return userInfo.alertTitle; } if (userAdded) { return Remove; } else { return 'Add'; } }, [canBeAdded, userAdded, userInfo.alertTitle]); return ( ); } export default AddMemberItem; diff --git a/web/modals/components/add-members.css b/web/modals/components/add-members.css index 96498af39..984e8a79a 100644 --- a/web/modals/components/add-members.css +++ b/web/modals/components/add-members.css @@ -1,36 +1,47 @@ div.addMemberItemsGroupHeader { font-size: var(--s-font-14); color: var(--add-members-group-header-color); margin: 16px; } button.addMemberItem { justify-content: space-between; color: var(--add-members-item-color); font-size: var(--l-font-18); width: 100%; } button.addMemberItem:hover { color: var(--add-members-item-color-hover); } button.addMemberItem:disabled { color: var(--add-members-item-disabled-color); cursor: not-allowed; } button.addMemberItem:hover:disabled { color: var(--add-members-item-disabled-color-hover); } button.addMemberItem .label { padding: 8px 16px; } button.addMemberItem .danger { color: var(--add-members-remove-pending-color); } button.addMemberItem:hover .danger { color: var(--add-members-remove-pending-color-hover); } + +div.userInfoContainer { + display: flex; + flex-direction: row; + align-items: center; + padding-left: 16px; +} + +div.username { + margin-left: 8px; +} diff --git a/web/modals/threads/members/member.react.js b/web/modals/threads/members/member.react.js index e8f6ea7d7..fc8467ce4 100644 --- a/web/modals/threads/members/member.react.js +++ b/web/modals/threads/members/member.react.js @@ -1,166 +1,169 @@ // @flow import classNames from 'classnames'; import * as React from 'react'; import { removeUsersFromThread, changeThreadMemberRoles, } from 'lib/actions/thread-actions.js'; import SWMansionIcon from 'lib/components/SWMansionIcon.react.js'; import { memberIsAdmin, memberHasAdminPowers, removeMemberFromThread, switchMemberAdminRoleInThread, getAvailableThreadMemberActions, } from 'lib/shared/thread-utils.js'; import { stringForUser } from 'lib/shared/user-utils.js'; import type { SetState } from 'lib/types/hook-types.js'; import { type RelativeMemberInfo, type ThreadInfo, } from 'lib/types/thread-types.js'; import { useDispatchActionPromise, useServerCall, } from 'lib/utils/action-utils.js'; import css from './members-modal.css'; import Label from '../../../components/label.react.js'; import MenuItem from '../../../components/menu-item.react.js'; import Menu from '../../../components/menu.react.js'; +import UserAvatar from '../../../components/user-avatar.react.js'; 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( () => getAvailableThreadMemberActions(memberInfo, threadInfo).map(action => { if (action === 'remove_admin') { return ( ); } if (action === 'make_admin') { return ( ); } if (action === 'remove_user') { return ( ); } return null; }), [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} + + {userName} + {label}
{menuItems}
); } export default ThreadMember;