diff --git a/web/components/menu.css b/web/components/menu.css index 897874772..3b5688b89 100644 --- a/web/components/menu.css +++ b/web/components/menu.css @@ -1,74 +1,81 @@ button.menuButton { background-color: transparent; border: none; cursor: pointer; color: inherit; } div.menuActionList { position: absolute; z-index: 4; display: flex; flex-direction: column; background-color: var(--menu-bg); color: var(--menu-color); border-radius: 4px; padding: 4px 0; line-height: var(--line-height-text); min-width: max-content; } div.menuActionListThreadActions { font-size: var(--m-font-16); top: 40px; right: -20px; } div.menuActionListMemberActions { font-size: var(--xs-font-12); background-color: var(--menu-bg-light); color: var(--menu-color-light); top: 0; right: 5px; } +div.menuActionListCommunityActions { + font-size: var(--m-font-16); + background-color: var(--menu-bg-light); + color: var(--menu-color); + top: 24px; +} + button.menuAction { color: inherit; z-index: 1; padding: 12px 16px; line-height: 1.5; font-size: inherit; justify-content: start; } button.menuAction:hover { color: var(--menu-color-hover); } div.menuActionIcon { display: flex; justify-content: center; margin-right: 8px; height: 24px; width: 24px; } div.menuActionListMemberActions div.menuActionIcon { height: 18px; width: 18px; } button.menuActionDangerous { color: var(--menu-color-dangerous); } button.menuActionDangerous:hover { color: var(--menu-color-dangerous-hover); } hr.separator { height: 1px; background: var(--menu-separator-color); margin: 10px 16px; max-width: 130px; border: none; } diff --git a/web/components/menu.react.js b/web/components/menu.react.js index 866bc0344..18b516ea8 100644 --- a/web/components/menu.react.js +++ b/web/components/menu.react.js @@ -1,119 +1,120 @@ // @flow import classnames from 'classnames'; import * as React from 'react'; import css from './menu.css'; import { useRenderMenu } from '../menu-provider.react.js'; -type MenuVariant = 'thread-actions' | 'member-actions'; +type MenuVariant = 'thread-actions' | 'member-actions' | 'community-actions'; type MenuProps = { +icon: React.Node, +children?: React.Node, +variant?: MenuVariant, +onChange?: boolean => void, }; function Menu(props: MenuProps): React.Node { const buttonRef = React.useRef(); const { renderMenu, setMenuPosition, closeMenu, setCurrentOpenMenu, currentOpenMenu, } = useRenderMenu(); const { icon, children, variant = 'thread-actions', onChange } = props; const ourSymbol = React.useRef(Symbol()); const menuActionListClasses = classnames(css.menuActionList, { [css.menuActionListThreadActions]: variant === 'thread-actions', [css.menuActionListMemberActions]: variant === 'member-actions', + [css.menuActionListCommunityActions]: variant === 'community-actions', }); const menuActionList = React.useMemo( () =>
{children}
, [children, menuActionListClasses], ); const isOurMenuOpen = currentOpenMenu === ourSymbol.current; const updatePosition = React.useCallback(() => { if (buttonRef.current && isOurMenuOpen) { const { top, left } = buttonRef.current.getBoundingClientRect(); setMenuPosition({ top, left }); } }, [isOurMenuOpen, setMenuPosition]); React.useEffect(() => { if (!window) { return undefined; } window.addEventListener('resize', updatePosition); return () => window.removeEventListener('resize', updatePosition); }, [updatePosition]); React.useEffect(updatePosition, [updatePosition]); const closeMenuCallback = React.useCallback(() => { closeMenu(ourSymbol.current); }, [closeMenu]); React.useEffect(() => { onChange?.(isOurMenuOpen); }, [isOurMenuOpen, onChange]); React.useEffect(() => { if (!isOurMenuOpen) { return undefined; } document.addEventListener('click', closeMenuCallback); return () => { document.removeEventListener('click', closeMenuCallback); }; }, [closeMenuCallback, isOurMenuOpen]); const prevActionListRef = React.useRef(null); React.useEffect(() => { if (!isOurMenuOpen) { prevActionListRef.current = null; return; } if (prevActionListRef.current === menuActionList) { return; } renderMenu(menuActionList); prevActionListRef.current = menuActionList; }, [isOurMenuOpen, menuActionList, renderMenu]); React.useEffect(() => { const ourSymbolValue = ourSymbol.current; return () => closeMenu(ourSymbolValue); }, [closeMenu]); const onClickMenuCallback = React.useCallback( e => { e.stopPropagation(); setCurrentOpenMenu(ourSymbol.current); }, [setCurrentOpenMenu], ); if (React.Children.count(children) === 0) { return null; } return ( ); } export default Menu; diff --git a/web/invite-links/invite-links-menu.css b/web/invite-links/invite-links-menu.css new file mode 100644 index 000000000..6b33c182b --- /dev/null +++ b/web/invite-links/invite-links-menu.css @@ -0,0 +1,3 @@ +.container { + color: var(--menu-color-light); +} diff --git a/web/invite-links/invite-links-menu.react.js b/web/invite-links/invite-links-menu.react.js new file mode 100644 index 000000000..38f22aa9e --- /dev/null +++ b/web/invite-links/invite-links-menu.react.js @@ -0,0 +1,38 @@ +// @flow + +import * as React from 'react'; + +import SWMansionIcon from 'lib/components/SWMansionIcon.react.js'; +import { primaryInviteLinksSelector } from 'lib/selectors/invite-links-selectors.js'; +import type { InviteLink } from 'lib/types/link-types.js'; + +import css from './invite-links-menu.css'; +import MenuItem from '../components/menu-item.react.js'; +import Menu from '../components/menu.react.js'; +import { useSelector } from '../redux/redux-utils.js'; + +type Props = { + +communityID: string, +}; + +function InviteLinksMenu(props: Props): React.Node { + const { communityID } = props; + const inviteLink: ?InviteLink = useSelector(primaryInviteLinksSelector)[ + communityID + ]; + + if (!inviteLink) { + return null; + } + + const icon = ; + return ( +
+ + + +
+ ); +} + +export default InviteLinksMenu; diff --git a/web/sidebar/community-drawer-item-community.react.js b/web/sidebar/community-drawer-item-community.react.js index 2bad1d8d1..912a27a81 100644 --- a/web/sidebar/community-drawer-item-community.react.js +++ b/web/sidebar/community-drawer-item-community.react.js @@ -1,98 +1,100 @@ // @flow import classnames from 'classnames'; import * as React from 'react'; import { useResolvedThreadInfo } from 'lib/utils/entity-helpers.js'; import { getCommunityDrawerItemCommunityHandler } from './community-drawer-item-community-handlers.react.js'; import css from './community-drawer-item.css'; import type { DrawerItemProps } from './community-drawer-item.react.js'; import { getChildren, getExpandButton, } from './community-drawer-utils.react.js'; import ThreadAvatar from '../avatars/thread-avatar.react.js'; +import InviteLinksMenu from '../invite-links/invite-links-menu.react.js'; function CommunityDrawerItemCommunity(props: DrawerItemProps): React.Node { const { itemData: { threadInfo, itemChildren, hasSubchannelsButton, labelStyle }, paddingLeft, expandable = true, handlerType, } = props; const Handler = getCommunityDrawerItemCommunityHandler(handlerType); const [handler, setHandler] = React.useState({ onClick: () => {}, isActive: false, expanded: false, toggleExpanded: () => {}, }); const children = React.useMemo( () => getChildren({ expanded: handler.expanded, hasSubchannelsButton, itemChildren, paddingLeft, threadInfo, expandable, handlerType, }), [ handler.expanded, hasSubchannelsButton, itemChildren, paddingLeft, threadInfo, expandable, handlerType, ], ); const itemExpandButton = React.useMemo( () => getExpandButton({ expandable, childrenLength: itemChildren?.length, hasSubchannelsButton, onExpandToggled: null, expanded: handler.expanded, }), [expandable, itemChildren?.length, hasSubchannelsButton, handler.expanded], ); const classes = classnames({ [css.communityBase]: true, [css.communityExpanded]: handler.expanded, }); const { uiName } = useResolvedThreadInfo(threadInfo); const titleLabel = classnames({ [css[labelStyle]]: true, [css.activeTitle]: handler.isActive, }); const style = React.useMemo(() => ({ paddingLeft }), [paddingLeft]); return (
{itemExpandButton}
{uiName}
+
{children}
); } const MemoizedCommunityDrawerItemCommunity: React.ComponentType = React.memo(CommunityDrawerItemCommunity); export default MemoizedCommunityDrawerItemCommunity;