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 (
);
}
const MemoizedCommunityDrawerItemCommunity: React.ComponentType =
React.memo(CommunityDrawerItemCommunity);
export default MemoizedCommunityDrawerItemCommunity;