move all menu to react-contexify

pull/1387/head
Audric Ackermann 5 years ago
parent 78843e81dd
commit fe3cfb9e82
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4

@ -41,13 +41,6 @@
opacity: 1;
}
.session-message {
.react-contextmenu-wrapper {
display: inline-flex;
width: 100%;
}
}
.public-chat-message-wrapper {
padding-inline-start: 10px;
padding-inline-end: 10px;

@ -2618,84 +2618,6 @@
outline: none;
}
// Third-party module: react-contextmenu
.react-contextmenu {
background-color: $color-light-02;
border-radius: 4px;
min-width: 160px;
padding: 0px;
padding-top: 8px;
padding-bottom: 8px;
border: 1px solid $color-dark-05;
opacity: 0;
user-select: none;
transition: opacity $session-transition-duration;
}
.react-contextmenu--visible {
z-index: 1000;
opacity: 1;
}
.react-contextmenu-item {
color: $color-gray-90;
cursor: pointer;
font-size: 13px;
line-height: 18px;
white-space: nowrap;
padding-inline-start: 16px;
padding-top: 3px;
padding-bottom: 2px;
padding-inline-end: 16px;
}
.react-contextmenu-item--checked:before {
content: '';
display: inline-block;
position: absolute;
right: 7px;
color: $color-gray-90;
}
.react-contextmenu-item.react-contextmenu-submenu {
padding: 0;
}
.react-contextmenu-item.react-contextmenu-submenu > .react-contextmenu-item {
padding-right: 36px;
// We will probably need to make this padding-inline-end once the whole app is working on rtl
}
.react-contextmenu-item.react-contextmenu-submenu
> .react-contextmenu-item:after {
content: '\25B6';
display: inline-block;
position: absolute;
right: 7px;
color: $color-gray-90;
}
.react-contextmenu-item.react-contextmenu-item--active,
.react-contextmenu-item.react-contextmenu-item--selected {
color: $color-white;
background-color: $color-light-35;
}
.react-contextmenu-item.react-contextmenu-item--active.react-contextmenu-item--checked:before,
.react-contextmenu-item.react-contextmenu-item--selected.react-contextmenu-item--checked:before {
color: $color-white;
}
.react-contextmenu-item.react-contextmenu-submenu
> .react-contextmenu-item.react-contextmenu-item--active:after,
.react-contextmenu-item.react-contextmenu-submenu
> .react-contextmenu-item.react-contextmenu-item--selected:after {
color: $color-white;
}
// All media query widths have 300px added to account for the left pane
// And have been tweaked for smoother transitions

@ -6,7 +6,7 @@ body.rtl {
.group-settings-item,
.contact-selection-list,
.group-member-list__selection,
.react-contextmenu-item,
.react-contexify__item,
.session-settings-list {
direction: rtl;
}

@ -753,41 +753,28 @@ label {
}
}
.react-contextmenu {
padding: 0px;
margin: 0px;
.react-contexify {
z-index: 3;
min-width: 200px;
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3), 0 0 0 1px #eee;
border: none !important;
border-radius: 0px;
}
.react-contextmenu-item {
display: flex;
align-items: center;
transition: $session-transition-duration;
height: 25px;
padding: $session-margin-md $session-margin-sm;
@include themify($themes) {
background: themed('contextMenuBackground');
color: themed('textColor');
}
font-family: $session-font-accent;
font-size: $session-font-sm;
line-height: $session-icon-size-sm;
font-weight: 700;
&--active,
&--selected {
.react-contexify__item:not(.react-contexify__item--disabled):hover
> .react-contexify__item__content {
@include themify($themes) {
background: themed('clickableHovered');
background: themed('accent');
color: themed('textColorOpposite');
}
}
&:active,
&:visited,
&:focus {
outline: none;
.react-contexify__item__content {
transition: $session-transition-duration;
}
&.react-contexify__submenu {
top: -28px !important; // height of an item element
}
.react-contexify__submenu-arrow {
line-height: 16px; // center the arrow for submenu
}
}

@ -197,11 +197,6 @@
letter-spacing: 0.03em;
margin-top: 3px;
margin-bottom: 3px;
.react-contextmenu-wrapper {
display: flex;
align-items: start;
}
}
.composition-container {

@ -1,6 +1,7 @@
// Modules
@import 'node_modules/emoji-mart/css/emoji-mart.css';
@import 'node_modules/react-h5-audio-player/lib/styles.css';
@import 'node_modules/react-contexify/dist/ReactContexify.min.css';
// Global Settings, Variables, and Mixins
@import 'themes.scss';

@ -1,8 +1,7 @@
import React from 'react';
import classNames from 'classnames';
import { isEmpty } from 'lodash';
import { ContextMenu, ContextMenuTrigger, MenuItem } from 'react-contextmenu';
import { Portal } from 'react-portal';
import { MenuProvider } from 'react-contexify';
import { Avatar } from './Avatar';
import { MessageBody } from './conversation/MessageBody';
@ -11,20 +10,15 @@ import { ContactName } from './conversation/ContactName';
import { TypingAnimation } from './conversation/TypingAnimation';
import { LocalizerType } from '../types/Util';
import {
getBlockMenuItem,
getClearNicknameMenuItem,
getCopyMenuItem,
getDeleteContactMenuItem,
getDeleteMessagesMenuItem,
getInviteContactMenuItem,
getLeaveGroupMenuItem,
} from '../session/utils/Menu';
import {
ConversationAvatar,
usingClosedConversationDetails,
} from './session/usingClosedConversationDetails';
import {
ConversationListItemContextMenu,
PropsContextConversationItem,
} from './session/menu/ConversationListItemContextMenu';
export type PropsData = {
id: string;
@ -159,86 +153,6 @@ class ConversationListItem extends React.PureComponent<Props> {
);
}
public renderContextMenu(triggerId: string) {
const {
i18n,
isBlocked,
isMe,
isClosable,
isRss,
isPublic,
hasNickname,
type,
isKickedFromGroup,
onDeleteContact,
onDeleteMessages,
onBlockContact,
onClearNickname,
onCopyPublicKey,
onUnblockContact,
onInviteContacts,
} = this.props;
const isPrivate = type === 'direct';
return (
<ContextMenu id={triggerId}>
{getBlockMenuItem(
isMe,
isPrivate,
isBlocked,
onBlockContact,
onUnblockContact,
i18n
)}
{/* {!isPublic && !isRss && !isMe ? (
<MenuItem onClick={onChangeNickname}>
{i18n('changeNickname')}
</MenuItem>
) : null} */}
{getClearNicknameMenuItem(
isPublic,
isRss,
isMe,
hasNickname,
onClearNickname,
i18n
)}
{getCopyMenuItem(
isPublic,
isRss,
type === 'group',
onCopyPublicKey,
i18n
)}
{getDeleteMessagesMenuItem(isPublic, onDeleteMessages, i18n)}
{getInviteContactMenuItem(
type === 'group',
isPublic,
onInviteContacts,
i18n
)}
{getDeleteContactMenuItem(
isMe,
isClosable,
type === 'group',
isPublic,
isRss,
onDeleteContact,
i18n
)}
{getLeaveGroupMenuItem(
isKickedFromGroup,
type === 'group',
isPublic,
isRss,
onDeleteContact,
i18n
)}
</ContextMenu>
);
}
public renderMessage() {
const { lastMessage, isTyping, unreadCount, i18n } = this.props;
@ -302,11 +216,12 @@ class ConversationListItem extends React.PureComponent<Props> {
style,
mentionedUs,
} = this.props;
const triggerId = `conversation-item-${phoneNumber}-ctxmenu-${Date.now()}`;
const triggerId = `conversation-item-${phoneNumber}-ctxmenu`;
const key = `conversation-item-${phoneNumber}`;
return (
<div key={triggerId}>
<ContextMenuTrigger id={triggerId}>
<div key={key}>
<MenuProvider id={triggerId}>
<div
role="button"
onClick={() => {
@ -333,12 +248,19 @@ class ConversationListItem extends React.PureComponent<Props> {
{this.renderMessage()}
</div>
</div>
</ContextMenuTrigger>
<Portal>{this.renderContextMenu(triggerId)}</Portal>
</MenuProvider>
<ConversationListItemContextMenu {...this.getMenuProps(triggerId)} />
</div>
);
}
private getMenuProps(triggerId: string): PropsContextConversationItem {
return {
triggerId,
...this.props,
};
}
private renderUser() {
const { name, phoneNumber, profileName, isMe, i18n } = this.props;

@ -1,10 +1,10 @@
import React from 'react';
import { Avatar } from '../Avatar';
import { Colors, LocalizerType } from '../../types/Util';
import { ContextMenu, ContextMenuTrigger, MenuItem } from 'react-contextmenu';
import { LocalizerType } from '../../types/Util';
import {
SessionIcon,
SessionIconButton,
SessionIconSize,
SessionIconType,
@ -15,11 +15,15 @@ import {
SessionButtonColor,
SessionButtonType,
} from '../session/SessionButton';
import * as Menu from '../../session/utils/Menu';
import {
ConversationAvatar,
usingClosedConversationDetails,
} from '../session/usingClosedConversationDetails';
import { MenuProvider } from 'react-contexify';
import {
ConversationHeaderMenu,
PropsConversationHeaderMenu,
} from '../session/menu/ConversationHeaderMenu';
export interface TimerOption {
name: string;
@ -93,24 +97,14 @@ interface Props {
}
class ConversationHeader extends React.Component<Props> {
public showMenuBound: (event: React.MouseEvent<HTMLDivElement>) => void;
public onAvatarClickBound: (userPubKey: string) => void;
public menuTriggerRef: React.RefObject<any>;
public constructor(props: Props) {
super(props);
this.menuTriggerRef = React.createRef();
this.showMenuBound = this.showMenu.bind(this);
this.onAvatarClickBound = this.onAvatarClick.bind(this);
}
public showMenu(event: React.MouseEvent<HTMLDivElement>) {
if (this.menuTriggerRef.current) {
this.menuTriggerRef.current.handleContextClick(event);
}
}
public renderBackButton() {
const { onGoBack, showBackButton } = this.props;
@ -246,90 +240,13 @@ class ConversationHeader extends React.Component<Props> {
if (showBackButton) {
return null;
}
return (
<ContextMenuTrigger
id={triggerId}
ref={this.menuTriggerRef}
holdToDisplay={1}
>
<MenuProvider id={triggerId} event="onClick">
<SessionIconButton
iconType={SessionIconType.Ellipses}
iconSize={SessionIconSize.Medium}
onClick={this.showMenuBound}
/>
</ContextMenuTrigger>
);
}
public renderMenu(triggerId: string) {
const {
i18n,
isMe,
isClosable,
isPublic,
isRss,
isGroup,
isKickedFromGroup,
amMod,
onDeleteMessages,
onDeleteContact,
onCopyPublicKey,
onLeaveGroup,
onAddModerators,
onRemoveModerators,
onInviteContacts,
onUpdateGroupName,
} = this.props;
return (
<ContextMenu id={triggerId}>
{this.renderPublicMenuItems()}
{Menu.getCopyMenuItem(isPublic, isRss, isGroup, onCopyPublicKey, i18n)}
{Menu.getDeleteMessagesMenuItem(isPublic, onDeleteMessages, i18n)}
{Menu.getAddModeratorsMenuItem(
amMod,
isKickedFromGroup,
onAddModerators,
i18n
)}
{Menu.getRemoveModeratorsMenuItem(
amMod,
isKickedFromGroup,
onRemoveModerators,
i18n
)}
{Menu.getUpdateGroupNameMenuItem(
amMod,
isKickedFromGroup,
onUpdateGroupName,
i18n
)}
{Menu.getLeaveGroupMenuItem(
isKickedFromGroup,
isGroup,
isPublic,
isRss,
onLeaveGroup,
i18n
)}
{/* TODO: add delete group */}
{Menu.getInviteContactMenuItem(
isGroup,
isPublic,
onInviteContacts,
i18n
)}
{Menu.getDeleteContactMenuItem(
isMe,
isClosable,
isGroup,
isPublic,
isRss,
onDeleteContact,
i18n
)}
</ContextMenu>
</MenuProvider>
);
}
@ -369,7 +286,7 @@ class ConversationHeader extends React.Component<Props> {
public render() {
const { id, isKickedFromGroup } = this.props;
const triggerId = `conversation-header-${id}-${Date.now()}`;
const triggerId = `conversation-header-${id}`;
const selectionMode = !!this.props.selectedMessages.length;
return (
@ -386,7 +303,7 @@ class ConversationHeader extends React.Component<Props> {
{!this.props.isRss && !selectionMode && this.renderAvatar()}
{!selectionMode && this.renderMenu(triggerId)}
<ConversationHeaderMenu {...this.getHeaderMenuProps(triggerId)} />
</div>
{selectionMode && this.renderSelectionOverlay()}
@ -406,68 +323,11 @@ class ConversationHeader extends React.Component<Props> {
($('.session-search-input input') as any).focus();
}
// tslint:disable-next-line: cyclomatic-complexity
private renderPublicMenuItems() {
const {
i18n,
isBlocked,
isMe,
isGroup,
isPrivate,
isKickedFromGroup,
isPublic,
isRss,
onResetSession,
onSetDisappearingMessages,
onShowSafetyNumber,
timerOptions,
onBlockUser,
onUnblockUser,
} = this.props;
const disappearingMessagesMenuItem = Menu.getDisappearingMenuItem(
isPublic,
isRss,
isKickedFromGroup,
isBlocked,
timerOptions,
onSetDisappearingMessages,
i18n
);
const showSafetyNumberMenuItem = Menu.getShowSafetyNumberMenuItem(
isPublic,
isRss,
isGroup,
isMe,
onShowSafetyNumber,
i18n
);
const resetSessionMenuItem = Menu.getResetSessionMenuItem(
isPublic,
isRss,
isGroup,
isBlocked,
onResetSession,
i18n
);
const blockHandlerMenuItem = Menu.getBlockMenuItem(
isMe,
isPrivate,
isBlocked,
onBlockUser,
onUnblockUser,
i18n
);
return (
<React.Fragment>
{disappearingMessagesMenuItem}
{showSafetyNumberMenuItem}
{resetSessionMenuItem}
{blockHandlerMenuItem}
</React.Fragment>
);
private getHeaderMenuProps(triggerId: string): PropsConversationHeaderMenu {
return {
triggerId,
...this.props,
};
}
}

@ -33,11 +33,9 @@ import { Contact } from '../../types/Contact';
import { getIncrement } from '../../util/timer';
import { isFileDangerous } from '../../util/isFileDangerous';
import { ColorType, LocalizerType } from '../../types/Util';
import { ContextMenu, ContextMenuTrigger, MenuItem } from 'react-contextmenu';
import { SessionIcon, SessionIconSize, SessionIconType } from '../session/icon';
import _ from 'lodash';
import { MessageModel } from '../../../js/models/messages';
import { animation, Item, Menu, MenuProvider, theme } from 'react-contexify';
declare global {
interface Window {
@ -133,7 +131,6 @@ const EXPIRED_DELAY = 600;
export class Message extends React.PureComponent<Props, State> {
public captureMenuTriggerBound: (trigger: any) => void;
public showMenuBound: (event: React.MouseEvent<HTMLDivElement>) => void;
public handleImageErrorBound: () => void;
public menuTriggerRef: Trigger | undefined;
@ -144,7 +141,6 @@ export class Message extends React.PureComponent<Props, State> {
super(props);
this.captureMenuTriggerBound = this.captureMenuTrigger.bind(this);
this.showMenuBound = this.showMenu.bind(this);
this.handleImageErrorBound = this.handleImageError.bind(this);
this.onReplyPrivate = this.onReplyPrivate.bind(this);
@ -805,7 +801,6 @@ export class Message extends React.PureComponent<Props, State> {
isDeletable,
onDelete,
onDownload,
onReply,
onRetrySend,
onShowDetail,
isPublic,
@ -821,8 +816,8 @@ export class Message extends React.PureComponent<Props, State> {
// Wraps a function to prevent event propagation, thus preventing
// message selection whenever any of the menu buttons are pressed.
const wrap = (f: any, ...args: Array<any>) => (event: Event) => {
event.stopPropagation();
const wrap = (f: any, ...args: Array<any>) => (e: any) => {
e.event.stopPropagation();
if (f) {
f(...args);
}
@ -847,72 +842,42 @@ export class Message extends React.PureComponent<Props, State> {
);
return (
<ContextMenu
<Menu
id={triggerId}
onShow={onContextMenuShown}
onHide={onContextMenuHidden}
onShown={onContextMenuShown}
onHidden={onContextMenuHidden}
animation={animation.fade}
>
{!multipleAttachments && attachments && attachments[0] ? (
<MenuItem
attributes={{
className: 'module-message__context__download',
}}
onClick={(e: Event) => {
e.stopPropagation();
<Item
onClick={(e: any) => {
e.event.stopPropagation();
if (onDownload) {
onDownload(isDangerous);
}
}}
>
{window.i18n('downloadAttachment')}
</MenuItem>
</Item>
) : null}
<MenuItem onClick={wrap(onCopyText)}>
{window.i18n('copyMessage')}
</MenuItem>
<MenuItem
attributes={{
className: 'module-message__context__reply',
}}
onClick={this.onReplyPrivate}
>
<Item onClick={wrap(onCopyText)}>{window.i18n('copyMessage')}</Item>
<Item onClick={this.onReplyPrivate}>
{window.i18n('replyToMessage')}
</MenuItem>
<MenuItem
attributes={{
className: 'module-message__context__more-info',
}}
onClick={wrap(onShowDetail)}
>
</Item>
<Item onClick={wrap(onShowDetail)}>
{window.i18n('moreInformation')}
</MenuItem>
</Item>
{showRetry ? (
<MenuItem
attributes={{
className: 'module-message__context__retry-send',
}}
onClick={wrap(onRetrySend)}
>
{window.i18n('resend')}
</MenuItem>
<Item onClick={wrap(onRetrySend)}>{window.i18n('resend')}</Item>
) : null}
{isDeletable ? (
<MenuItem
attributes={{
className: 'module-message__context__delete-message',
}}
onClick={wrap(onDelete)}
>
{deleteMessageCtxText}
</MenuItem>
<Item onClick={wrap(onDelete)}>{deleteMessageCtxText}</Item>
) : null}
{isModerator && isPublic ? (
<MenuItem onClick={wrap(onBanUser)}>
{window.i18n('banUser')}
</MenuItem>
<Item onClick={wrap(onBanUser)}>{window.i18n('banUser')}</Item>
) : null}
</ContextMenu>
</Menu>
);
}
@ -1036,7 +1001,7 @@ export class Message extends React.PureComponent<Props, State> {
return (
<div id={id} className={classNames(divClasses)}>
<ContextMenuTrigger id={rightClickTriggerId}>
<MenuProvider id={rightClickTriggerId}>
{this.renderAvatar()}
<div
className={classNames(
@ -1105,7 +1070,7 @@ export class Message extends React.PureComponent<Props, State> {
? this.renderContextMenu(rightClickTriggerId)
: null}
</div>
</ContextMenuTrigger>
</MenuProvider>
</div>
);
}
@ -1147,8 +1112,8 @@ export class Message extends React.PureComponent<Props, State> {
);
}
private onReplyPrivate(e: Event) {
e.stopPropagation();
private onReplyPrivate(e: any) {
e.event.stopPropagation();
if (this.props && this.props.onReply) {
this.props.onReply(this.props.timestamp);
}

@ -6,6 +6,7 @@ import { PropsData as ConversationListItemPropsType } from '../ConversationListI
import { createOrUpdateItem, getItemById } from '../../../js/modules/data';
import { APPLY_THEME } from '../../state/ducks/theme';
import { darkTheme, lightTheme } from '../../state/ducks/SessionTheme';
// tslint:disable-next-line: no-import-side-effect no-submodule-imports
export enum SectionType {
Profile,

@ -1,4 +1,4 @@
import styled, { css } from 'styled-components';
import styled from 'styled-components';
export interface FlexProps {
children?: any;

@ -1,6 +1,6 @@
import React from 'react';
import { animation, Item, Menu, MenuProvider } from 'react-contexify';
import { SessionIconButton, SessionIconSize, SessionIconType } from './icon';
import { ContextMenu, ContextMenuTrigger, MenuItem } from 'react-contextmenu';
interface Props {
searchString: string;
@ -21,7 +21,7 @@ export class SessionSearchInput extends React.Component<Props> {
return (
<>
<ContextMenuTrigger id={triggerId}>
<MenuProvider id={triggerId}>
<div className="session-search-input">
<SessionIconButton
iconSize={SessionIconSize.Medium}
@ -34,28 +34,28 @@ export class SessionSearchInput extends React.Component<Props> {
placeholder={this.props.placeholder}
/>
</div>
</ContextMenuTrigger>
<ContextMenu id={triggerId}>
<MenuItem onClick={() => document.execCommand('undo')}>
</MenuProvider>
<Menu id={triggerId} animation={animation.fade}>
<Item onClick={() => document.execCommand('undo')}>
{window.i18n('editMenuUndo')}
</MenuItem>
<MenuItem onClick={() => document.execCommand('redo')}>
</Item>
<Item onClick={() => document.execCommand('redo')}>
{window.i18n('editMenuRedo')}
</MenuItem>
</Item>
<hr />
<MenuItem onClick={() => document.execCommand('cut')}>
<Item onClick={() => document.execCommand('cut')}>
{window.i18n('editMenuCut')}
</MenuItem>
<MenuItem onClick={() => document.execCommand('copy')}>
</Item>
<Item onClick={() => document.execCommand('copy')}>
{window.i18n('editMenuCopy')}
</MenuItem>
<MenuItem onClick={() => document.execCommand('paste')}>
</Item>
<Item onClick={() => document.execCommand('paste')}>
{window.i18n('editMenuPaste')}
</MenuItem>
<MenuItem onClick={() => document.execCommand('selectAll')}>
</Item>
<Item onClick={() => document.execCommand('selectAll')}>
{window.i18n('editMenuSelectAll')}
</MenuItem>
</ContextMenu>
</Item>
</Menu>
</>
);
}

@ -5,14 +5,13 @@ import { Props, SessionIcon } from '../icon';
import { SessionNotificationCount } from '../SessionNotificationCount';
interface SProps extends Props {
onClick: any;
onClick?: any;
notificationCount?: number;
isSelected: boolean;
}
export class SessionIconButton extends React.PureComponent<SProps> {
public static readonly extendedDefaults = {
onClick: () => null,
notificationCount: undefined,
isSelected: false,
};

@ -0,0 +1,158 @@
import React from 'react';
import { animation, Menu } from 'react-contexify';
import {
getAddModeratorsMenuItem,
getBlockMenuItem,
getCopyMenuItem,
getDeleteContactMenuItem,
getDeleteMessagesMenuItem,
getDisappearingMenuItem,
getInviteContactMenuItem,
getLeaveGroupMenuItem,
getRemoveModeratorsMenuItem,
getResetSessionMenuItem,
getShowSafetyNumberMenuItem,
getUpdateGroupNameMenuItem,
} from './Menu';
import { TimerOption } from '../../conversation/ConversationHeader';
export type PropsConversationHeaderMenu = {
triggerId: string;
isMe: boolean;
isPublic?: boolean;
isRss?: boolean;
isClosable?: boolean;
isKickedFromGroup?: boolean;
isGroup: boolean;
amMod: boolean;
timerOptions: Array<TimerOption>;
isPrivate: boolean;
isBlocked: boolean;
onDeleteMessages?: () => void;
onDeleteContact?: () => void;
onCopyPublicKey?: () => void;
onInviteContacts?: () => void;
onLeaveGroup: () => void;
onAddModerators: () => void;
onRemoveModerators: () => void;
onUpdateGroupName: () => void;
onBlockUser: () => void;
onUnblockUser: () => void;
onShowSafetyNumber: () => void;
onSetDisappearingMessages: (seconds: number) => void;
onResetSession: () => void;
};
export const ConversationHeaderMenu = (props: PropsConversationHeaderMenu) => {
const {
triggerId,
isMe,
isClosable,
isPublic,
isRss,
isGroup,
isKickedFromGroup,
amMod,
timerOptions,
isBlocked,
isPrivate,
onDeleteMessages,
onDeleteContact,
onCopyPublicKey,
onLeaveGroup,
onAddModerators,
onRemoveModerators,
onInviteContacts,
onUpdateGroupName,
onBlockUser,
onUnblockUser,
onShowSafetyNumber,
onResetSession,
onSetDisappearingMessages,
} = props;
return (
<Menu id={triggerId} animation={animation.fade}>
{getDisappearingMenuItem(
isPublic,
isRss,
isKickedFromGroup,
isBlocked,
timerOptions,
onSetDisappearingMessages,
window.i18n
)}
{getShowSafetyNumberMenuItem(
isPublic,
isRss,
isGroup,
isMe,
onShowSafetyNumber,
window.i18n
)}
{getResetSessionMenuItem(
isPublic,
isRss,
isGroup,
isBlocked,
onResetSession,
window.i18n
)}
{getBlockMenuItem(
isMe,
isPrivate,
isBlocked,
onBlockUser,
onUnblockUser,
window.i18n
)}
{getCopyMenuItem(isPublic, isRss, isGroup, onCopyPublicKey, window.i18n)}
{getDeleteMessagesMenuItem(isPublic, onDeleteMessages, window.i18n)}
{getAddModeratorsMenuItem(
amMod,
isKickedFromGroup,
onAddModerators,
window.i18n
)}
{getRemoveModeratorsMenuItem(
amMod,
isKickedFromGroup,
onRemoveModerators,
window.i18n
)}
{getUpdateGroupNameMenuItem(
amMod,
isKickedFromGroup,
onUpdateGroupName,
window.i18n
)}
{getLeaveGroupMenuItem(
isKickedFromGroup,
isGroup,
isPublic,
isRss,
onLeaveGroup,
window.i18n
)}
{/* TODO: add delete group */}
{getInviteContactMenuItem(
isGroup,
isPublic,
onInviteContacts,
window.i18n
)}
{getDeleteContactMenuItem(
isMe,
isClosable,
isGroup,
isPublic,
isRss,
onDeleteContact,
window.i18n
)}
</Menu>
);
};

@ -0,0 +1,112 @@
import React from 'react';
import { animation, Menu } from 'react-contexify';
import {
getBlockMenuItem,
getClearNicknameMenuItem,
getCopyMenuItem,
getDeleteContactMenuItem,
getDeleteMessagesMenuItem,
getInviteContactMenuItem,
getLeaveGroupMenuItem,
} from './Menu';
export type PropsContextConversationItem = {
triggerId: string;
type: 'group' | 'direct';
isMe: boolean;
isPublic?: boolean;
isRss?: boolean;
isClosable?: boolean;
isBlocked?: boolean;
hasNickname?: boolean;
isKickedFromGroup?: boolean;
onDeleteMessages?: () => void;
onDeleteContact?: () => void;
onBlockContact?: () => void;
onCopyPublicKey?: () => void;
onUnblockContact?: () => void;
onInviteContacts?: () => void;
onClearNickname?: () => void;
};
export const ConversationListItemContextMenu = (
props: PropsContextConversationItem
) => {
const {
triggerId,
isBlocked,
isMe,
isClosable,
isRss,
isPublic,
hasNickname,
type,
isKickedFromGroup,
onDeleteContact,
onDeleteMessages,
onBlockContact,
onClearNickname,
onCopyPublicKey,
onUnblockContact,
onInviteContacts,
} = props;
return (
<Menu id={triggerId} animation={animation.fade}>
{getBlockMenuItem(
isMe,
type === 'direct',
isBlocked,
onBlockContact,
onUnblockContact,
window.i18n
)}
{/* {!isPublic && !isRss && !isMe ? (
<Item onClick={onChangeNickname}>
{i18n('changeNickname')}
</Item>
) : null} */}
{getClearNicknameMenuItem(
isPublic,
isRss,
isMe,
hasNickname,
onClearNickname,
window.i18n
)}
{getCopyMenuItem(
isPublic,
isRss,
type === 'group',
onCopyPublicKey,
window.i18n
)}
{getDeleteMessagesMenuItem(isPublic, onDeleteMessages, window.i18n)}
{getInviteContactMenuItem(
type === 'group',
isPublic,
onInviteContacts,
window.i18n
)}
{getDeleteContactMenuItem(
isMe,
isClosable,
type === 'group',
isPublic,
isRss,
onDeleteContact,
window.i18n
)}
{getLeaveGroupMenuItem(
isKickedFromGroup,
type === 'group',
isPublic,
isRss,
onDeleteContact,
window.i18n
)}
</Menu>
);
};

@ -1,7 +1,7 @@
import React from 'react';
import { MenuItem, SubMenu } from 'react-contextmenu';
import { LocalizerType } from '../../types/Util';
import { TimerOption } from '../../components/conversation/ConversationHeader';
import { LocalizerType } from '../../../types/Util';
import { TimerOption } from '../../conversation/ConversationHeader';
import { Item, Submenu } from 'react-contexify';
function showTimerOptions(
isPublic: boolean,
@ -116,7 +116,7 @@ export function getInviteContactMenuItem(
i18n: LocalizerType
): JSX.Element | null {
if (showInviteContact(Boolean(isGroup), Boolean(isPublic))) {
return <MenuItem onClick={action}>{i18n('inviteContacts')}</MenuItem>;
return <Item onClick={action}>{i18n('inviteContacts')}</Item>;
}
return null;
}
@ -140,9 +140,9 @@ export function getDeleteContactMenuItem(
)
) {
if (isPublic) {
return <MenuItem onClick={action}>{i18n('leaveGroup')}</MenuItem>;
return <Item onClick={action}>{i18n('leaveGroup')}</Item>;
}
return <MenuItem onClick={action}>{i18n('delete')}</MenuItem>;
return <Item onClick={action}>{i18n('delete')}</Item>;
}
return null;
}
@ -163,7 +163,7 @@ export function getLeaveGroupMenuItem(
Boolean(isRss)
)
) {
return <MenuItem onClick={action}>{i18n('leaveGroup')}</MenuItem>;
return <Item onClick={action}>{i18n('leaveGroup')}</Item>;
}
return null;
}
@ -175,7 +175,7 @@ export function getUpdateGroupNameMenuItem(
i18n: LocalizerType
): JSX.Element | null {
if (showUpdateGroupName(Boolean(amMod), Boolean(isKickedFromGroup))) {
return <MenuItem onClick={action}>{i18n('editGroup')}</MenuItem>;
return <Item onClick={action}>{i18n('editGroup')}</Item>;
}
return null;
}
@ -187,7 +187,7 @@ export function getRemoveModeratorsMenuItem(
i18n: LocalizerType
): JSX.Element | null {
if (showRemoveModerators(Boolean(amMod), Boolean(isKickedFromGroup))) {
return <MenuItem onClick={action}>{i18n('removeModerators')}</MenuItem>;
return <Item onClick={action}>{i18n('removeModerators')}</Item>;
}
return null;
}
@ -199,7 +199,7 @@ export function getAddModeratorsMenuItem(
i18n: LocalizerType
): JSX.Element | null {
if (showAddModerators(Boolean(amMod), Boolean(isKickedFromGroup))) {
return <MenuItem onClick={action}>{i18n('addModerators')}</MenuItem>;
return <Item onClick={action}>{i18n('addModerators')}</Item>;
}
return null;
}
@ -213,7 +213,7 @@ export function getCopyMenuItem(
): JSX.Element | null {
if (showCopyId(Boolean(isPublic), Boolean(isRss), Boolean(isGroup))) {
const copyIdLabel = i18n('copySessionID');
return <MenuItem onClick={action}>{copyIdLabel}</MenuItem>;
return <Item onClick={action}>{copyIdLabel}</Item>;
}
return null;
}
@ -238,21 +238,21 @@ export function getDisappearingMenuItem(
const isRtlMode = isRtlBody();
return (
// Remove the && false to make context menu work with RTL support
<SubMenu
title={i18n('disappearingMessages') as any}
rtl={isRtlMode && false}
<Submenu
label={i18n('disappearingMessages') as any}
// rtl={isRtlMode && false}
>
{(timerOptions || []).map(item => (
<MenuItem
<Item
key={item.value}
onClick={() => {
action(item.value);
}}
>
{item.name}
</MenuItem>
</Item>
))}
</SubMenu>
</Submenu>
);
}
return null;
@ -270,7 +270,7 @@ export function getShowMemberMenuItem(
i18n: LocalizerType
): JSX.Element | null {
if (showMemberMenu(Boolean(isPublic), Boolean(isRss), Boolean(isGroup))) {
return <MenuItem onClick={action}>{i18n('groupMembers')}</MenuItem>;
return <Item onClick={action}>{i18n('groupMembers')}</Item>;
}
return null;
}
@ -291,7 +291,7 @@ export function getShowSafetyNumberMenuItem(
Boolean(isMe)
)
) {
return <MenuItem onClick={action}>{i18n('showSafetyNumber')}</MenuItem>;
return <Item onClick={action}>{i18n('showSafetyNumber')}</Item>;
}
return null;
}
@ -312,7 +312,7 @@ export function getResetSessionMenuItem(
Boolean(isBlocked)
)
) {
return <MenuItem onClick={action}>{i18n('resetSession')}</MenuItem>;
return <Item onClick={action}>{i18n('resetSession')}</Item>;
}
return null;
}
@ -328,7 +328,7 @@ export function getBlockMenuItem(
if (showBlock(Boolean(isMe), Boolean(isPrivate))) {
const blockTitle = isBlocked ? i18n('unblockUser') : i18n('blockUser');
const blockHandler = isBlocked ? actionUnblock : actionBlock;
return <MenuItem onClick={blockHandler}>{blockTitle}</MenuItem>;
return <Item onClick={blockHandler}>{blockTitle}</Item>;
}
return null;
}
@ -349,7 +349,7 @@ export function getClearNicknameMenuItem(
Boolean(hasNickname)
)
) {
return <MenuItem onClick={action}>{i18n('clearNickname')}</MenuItem>;
return <Item onClick={action}>{i18n('clearNickname')}</Item>;
}
return null;
}
@ -360,7 +360,7 @@ export function getDeleteMessagesMenuItem(
i18n: LocalizerType
): JSX.Element | null {
if (showDeleteMessages(Boolean(isPublic))) {
return <MenuItem onClick={action}>{i18n('deleteMessages')}</MenuItem>;
return <Item onClick={action}>{i18n('deleteMessages')}</Item>;
}
return null;
}

@ -5,7 +5,7 @@ import * as StringUtils from './String';
import * as NumberUtils from './Number';
import * as PromiseUtils from './Promise';
import * as ProtobufUtils from './Protobuf';
import * as MenuUtils from './Menu';
import * as MenuUtils from '../../components/session/menu/Menu';
import * as ToastUtils from './Toast';
export * from './Attachments';

Loading…
Cancel
Save