make standardized menu, use them in ConversationHeader

and ConversationListItem
This commit is contained in:
Audric Ackermann 2020-07-24 16:38:03 +10:00
parent 8910be2ceb
commit 401c37c39e
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4
5 changed files with 471 additions and 229 deletions

View File

@ -608,6 +608,7 @@
}, },
isOnline: this.isOnline(), isOnline: this.isOnline(),
hasNickname: !!this.getNickname(), hasNickname: !!this.getNickname(),
isKickedFromGroup: !!this.get('isKickedFromGroup'),
selectedMessages: this.selectedMessages, selectedMessages: this.selectedMessages,
@ -620,6 +621,9 @@
onDeleteContact: () => this.deleteContact(), onDeleteContact: () => this.deleteContact(),
onDeleteMessages: () => this.deleteMessages(), onDeleteMessages: () => this.deleteMessages(),
onCloseOverlay: () => this.resetMessageSelection(), onCloseOverlay: () => this.resetMessageSelection(),
onInviteContacts: () => {
window.Whisper.events.trigger('inviteContacts', this);
},
}; };
return result; return result;

View File

@ -12,10 +12,12 @@ import { TypingAnimation } from './conversation/TypingAnimation';
import { Colors, LocalizerType } from '../types/Util'; import { Colors, LocalizerType } from '../types/Util';
import { import {
showClearNickname, getBlockMenuItem,
showBlock, getClearNicknameMenuItem,
showCopyId, getCopyIdMenuItem,
showDeleteContact, getDeleteContactMenuItem,
getInviteContactMenuItem,
getLeaveGroupMenuItem,
} from '../session/utils/Menu'; } from '../session/utils/Menu';
export type PropsData = { export type PropsData = {
@ -49,6 +51,7 @@ export type PropsData = {
hasNickname?: boolean; hasNickname?: boolean;
isSecondary?: boolean; isSecondary?: boolean;
isGroupInvitation?: boolean; isGroupInvitation?: boolean;
isKickedFromGroup?: boolean;
}; };
type PropsHousekeeping = { type PropsHousekeeping = {
@ -62,6 +65,7 @@ type PropsHousekeeping = {
onClearNickname?: () => void; onClearNickname?: () => void;
onCopyPublicKey?: () => void; onCopyPublicKey?: () => void;
onUnblockContact?: () => void; onUnblockContact?: () => void;
onInviteContacts?: () => void;
}; };
type Props = PropsData & PropsHousekeeping; type Props = PropsData & PropsHousekeeping;
@ -170,53 +174,72 @@ export class ConversationListItem extends React.PureComponent<Props> {
isPublic, isPublic,
hasNickname, hasNickname,
type, type,
isKickedFromGroup,
onDeleteContact, onDeleteContact,
onDeleteMessages, onDeleteMessages,
onBlockContact, onBlockContact,
onChangeNickname,
onClearNickname, onClearNickname,
onCopyPublicKey, onCopyPublicKey,
onUnblockContact, onUnblockContact,
onInviteContacts,
} = this.props; } = this.props;
const blockTitle = isBlocked ? i18n('unblockUser') : i18n('blockUser');
const blockHandler = isBlocked ? onUnblockContact : onBlockContact;
const isPrivate = type === 'direct'; const isPrivate = type === 'direct';
return ( return (
<ContextMenu id={triggerId}> <ContextMenu id={triggerId}>
{showBlock(isMe, isPrivate) ? ( {getBlockMenuItem(
<MenuItem onClick={blockHandler}>{blockTitle}</MenuItem> isMe,
) : null} isPrivate,
isBlocked,
onBlockContact,
onUnblockContact,
i18n
)}
{/* {!isPublic && !isRss && !isMe ? ( {/* {!isPublic && !isRss && !isMe ? (
<MenuItem onClick={onChangeNickname}> <MenuItem onClick={onChangeNickname}>
{i18n('changeNickname')} {i18n('changeNickname')}
</MenuItem> </MenuItem>
) : null} */} ) : null} */}
{showClearNickname(isPublic, isRss, isMe, hasNickname) ? ( {getClearNicknameMenuItem(
<MenuItem onClick={onClearNickname}>{i18n('clearNickname')}</MenuItem> isPublic,
) : null} isRss,
{showCopyId(isPublic, isRss) ? ( isMe,
<MenuItem onClick={onCopyPublicKey}>{i18n('copyPublicKey')}</MenuItem> hasNickname,
) : null} onClearNickname,
i18n
)}
{getCopyIdMenuItem(
isPublic,
isRss,
type === 'group',
onCopyPublicKey,
i18n
)}
<MenuItem onClick={onDeleteMessages}>{i18n('deleteMessages')}</MenuItem> <MenuItem onClick={onDeleteMessages}>{i18n('deleteMessages')}</MenuItem>
{showDeleteContact( {getInviteContactMenuItem(
type === 'group',
isPublic,
onInviteContacts,
i18n
)}
{getDeleteContactMenuItem(
isMe, isMe,
isClosable, isClosable,
type === 'group', type === 'group',
isPublic, isPublic,
isRss isRss,
) ? ( onDeleteContact,
!isPublic ? ( i18n
<MenuItem onClick={onDeleteContact}> )}
{i18n('deleteContact')} {getLeaveGroupMenuItem(
</MenuItem> isKickedFromGroup,
) : ( type === 'group',
<MenuItem onClick={onDeleteContact}> isPublic,
{i18n('deletePublicChannel')} isRss,
</MenuItem> onDeleteContact,
) i18n
) : null} )}
</ContextMenu> </ContextMenu>
); );
} }

View File

@ -20,20 +20,7 @@ import {
SessionButtonColor, SessionButtonColor,
SessionButtonType, SessionButtonType,
} from '../session/SessionButton'; } from '../session/SessionButton';
import { import * as Menu from '../../session/utils/Menu';
showAddModerators,
showBlock,
showCopyId,
showDeleteContact,
showInviteContact,
showLeaveGroup,
showMemberMenu,
showRemoveModerators,
showResetSession,
showSafetyNumber,
showTimerOptions,
showUpdateGroupName,
} from '../../session/utils/Menu';
export interface TimerOption { export interface TimerOption {
name: string; name: string;
@ -319,48 +306,59 @@ export class ConversationHeader extends React.Component<Props> {
onUpdateGroupName, onUpdateGroupName,
} = this.props; } = this.props;
const copyIdLabel = isGroup ? i18n('copyChatId') : i18n('copyPublicKey');
return ( return (
<ContextMenu id={triggerId}> <ContextMenu id={triggerId}>
{this.renderPublicMenuItems()} {this.renderPublicMenuItems()}
{showCopyId(isPublic, isRss) ? ( {Menu.getCopyIdMenuItem(
<MenuItem onClick={onCopyPublicKey}>{copyIdLabel}</MenuItem> isPublic,
) : null} isRss,
isGroup,
onCopyPublicKey,
i18n
)}
<MenuItem onClick={onDeleteMessages}>{i18n('deleteMessages')}</MenuItem> <MenuItem onClick={onDeleteMessages}>{i18n('deleteMessages')}</MenuItem>
{showAddModerators(amMod, isKickedFromGroup) ? ( {Menu.getAddModeratorsMenuItem(
<MenuItem onClick={onAddModerators}>{i18n('addModerators')}</MenuItem> amMod,
) : null} isKickedFromGroup,
{showRemoveModerators(amMod, isKickedFromGroup) ? ( onAddModerators,
<MenuItem onClick={onRemoveModerators}> i18n
{i18n('removeModerators')} )}
</MenuItem> {Menu.getRemoveModeratorsMenuItem(
) : null} amMod,
{showUpdateGroupName(amMod, isKickedFromGroup) ? ( isKickedFromGroup,
<MenuItem onClick={onUpdateGroupName}> onRemoveModerators,
{i18n('editGroupNameOrPicture')} i18n
</MenuItem> )}
) : null} {Menu.getUpdateGroupNameMenuItem(
{showLeaveGroup(isKickedFromGroup, isGroup, isPublic, isRss) ? ( amMod,
<MenuItem onClick={onLeaveGroup}>{i18n('leaveGroup')}</MenuItem> isKickedFromGroup,
) : null} onUpdateGroupName,
i18n
)}
{Menu.getLeaveGroupMenuItem(
isKickedFromGroup,
isGroup,
isPublic,
isRss,
onLeaveGroup,
i18n
)}
{/* TODO: add delete group */} {/* TODO: add delete group */}
{showInviteContact(isGroup, isPublic) ? ( {Menu.getInviteContactMenuItem(
<MenuItem onClick={onInviteContacts}> isGroup,
{i18n('inviteContacts')} isPublic,
</MenuItem> onInviteContacts,
) : null} i18n
{showDeleteContact(isMe, isClosable, isGroup, isPublic, isRss) ? ( )}
!isPublic ? ( {Menu.getDeleteContactMenuItem(
<MenuItem onClick={onDeleteContact}> isMe,
{i18n('deleteContact')} isClosable,
</MenuItem> isGroup,
) : ( isPublic,
<MenuItem onClick={onDeleteContact}> isRss,
{i18n('deletePublicChannel')} onDeleteContact,
</MenuItem> i18n
) )}
) : null}
</ContextMenu> </ContextMenu>
); );
} }
@ -459,53 +457,45 @@ export class ConversationHeader extends React.Component<Props> {
onUnblockUser, onUnblockUser,
} = this.props; } = this.props;
if (isPublic || isRss) { const disappearingMessagesMenuItem = Menu.getDisappearingMenuItem(
return null;
}
const disappearingTitle = i18n('disappearingMessages') as any;
const blockTitle = isBlocked ? i18n('unblockUser') : i18n('blockUser');
const blockHandler = isBlocked ? onUnblockUser : onBlockUser;
const disappearingMessagesMenuItem = showTimerOptions(
isPublic, isPublic,
isRss, isRss,
isKickedFromGroup, isKickedFromGroup,
isBlocked isBlocked,
) && ( timerOptions,
<SubMenu title={disappearingTitle}> onSetDisappearingMessages,
{(timerOptions || []).map(item => ( i18n
<MenuItem
key={item.value}
onClick={() => {
onSetDisappearingMessages(item.value);
}}
>
{item.name}
</MenuItem>
))}
</SubMenu>
); );
const showMembersMenuItem = showMemberMenu(isPublic, isRss, isGroup) && ( const showMembersMenuItem = Menu.getShowMemberMenuItem(
<MenuItem onClick={onShowGroupMembers}>{i18n('showMembers')}</MenuItem>
);
const showSafetyNumberMenuItem = showSafetyNumber(
isPublic, isPublic,
isRss, isRss,
isGroup, isGroup,
isMe onShowGroupMembers,
) && ( i18n
<MenuItem onClick={onShowSafetyNumber}>
{i18n('showSafetyNumber')}
</MenuItem>
); );
const resetSessionMenuItem = showResetSession(isPublic, isRss, isGroup) && (
<MenuItem onClick={onResetSession}>{i18n('resetSession')}</MenuItem> const showSafetyNumberMenuItem = Menu.getShowSafetyNumberMenuItem(
isPublic,
isRss,
isGroup,
isMe,
onShowSafetyNumber,
i18n
); );
const blockHandlerMenuItem = showBlock(isMe, isPrivate) && ( const resetSessionMenuItem = Menu.getResetSessionMenuItem(
<MenuItem onClick={blockHandler}>{blockTitle}</MenuItem> isPublic,
isRss,
isGroup,
onResetSession,
i18n
);
const blockHandlerMenuItem = Menu.getBlockMenuItem(
isMe,
isPrivate,
isBlocked,
onBlockUser,
onUnblockUser,
i18n
); );
return ( return (

View File

@ -1,111 +0,0 @@
export function showTimerOptions(
isPublic: boolean,
isRss: boolean,
isKickedFromGroup: boolean,
isBlocked: boolean
): boolean {
return (
Boolean(!isPublic) && Boolean(!isRss) && !isKickedFromGroup && !isBlocked
);
}
export function showMemberMenu(
isPublic: boolean,
isRss: boolean,
isGroup: boolean
): boolean {
return Boolean(!isPublic) && Boolean(!isRss) && isGroup;
}
export function showSafetyNumber(
isPublic: boolean,
isRss: boolean,
isGroup: boolean,
isMe: boolean
): boolean {
return Boolean(!isPublic) && Boolean(!isRss) && !isGroup && !isMe;
}
export function showResetSession(
isPublic: boolean,
isRss: boolean,
isGroup: boolean
): boolean {
return Boolean(!isPublic) && Boolean(!isRss) && Boolean(!isGroup);
}
export function showBlock(
isMe: boolean | undefined,
isPrivate: boolean | undefined
): boolean {
return Boolean(!isMe) && Boolean(isPrivate);
}
export function showClearNickname(
isPublic: boolean | undefined,
isRss: boolean | undefined,
isMe: boolean | undefined,
hasNickname: boolean | undefined
): boolean {
return (
Boolean(!isPublic) &&
Boolean(!isRss) &&
Boolean(!isMe) &&
Boolean(hasNickname)
);
}
export function showCopyId(
isPublic: boolean | undefined,
isRss: boolean | undefined
): boolean {
return Boolean(!isPublic) && Boolean(!isRss);
}
export function showDeleteContact(
isMe: boolean | undefined,
isClosable: boolean | undefined,
isGroup: boolean | undefined,
isPublic: boolean | undefined,
isRss: boolean | undefined
): boolean {
return (
Boolean(!isMe) && Boolean(isClosable) && !!(!isGroup || isPublic || isRss)
);
}
export function showAddModerators(
amMod: boolean | undefined,
isKickedFromGroup: boolean | undefined
): boolean {
return Boolean(!isKickedFromGroup) && Boolean(amMod);
}
export function showRemoveModerators(
amMod: boolean | undefined,
isKickedFromGroup: boolean | undefined
): boolean {
return Boolean(!isKickedFromGroup) && Boolean(amMod);
}
export function showUpdateGroupName(
amMod: boolean | undefined,
isKickedFromGroup: boolean | undefined
): boolean {
return Boolean(!isKickedFromGroup) && Boolean(amMod);
}
export function showLeaveGroup(
isKickedFromGroup: boolean | undefined,
isGroup: boolean | undefined,
isPublic: boolean | undefined,
isRss: boolean | undefined
): boolean {
return Boolean(!isKickedFromGroup) && !!(!isGroup || isPublic || isRss);
}
export function showInviteContact(
isGroup: boolean | undefined,
isPublic: boolean | undefined
): boolean {
return Boolean(isGroup) && Boolean(isPublic);
}

336
ts/session/utils/Menu.tsx Normal file
View File

@ -0,0 +1,336 @@
import React from 'react';
import { MenuItem, SubMenu } from 'react-contextmenu';
import { LocalizerType } from '../../types/Util';
import { TimerOption } from '../../components/conversation/ConversationHeader';
function showTimerOptions(
isPublic: boolean,
isRss: boolean,
isKickedFromGroup: boolean,
isBlocked: boolean
): boolean {
return (
Boolean(!isPublic) && Boolean(!isRss) && !isKickedFromGroup && !isBlocked
);
}
function showMemberMenu(
isPublic: boolean,
isRss: boolean,
isGroup: boolean
): boolean {
return Boolean(!isPublic) && Boolean(!isRss) && isGroup;
}
function showSafetyNumber(
isPublic: boolean,
isRss: boolean,
isGroup: boolean,
isMe: boolean
): boolean {
return Boolean(!isPublic) && Boolean(!isRss) && !isGroup && !isMe;
}
function showResetSession(
isPublic: boolean,
isRss: boolean,
isGroup: boolean
): boolean {
return Boolean(!isPublic) && Boolean(!isRss) && Boolean(!isGroup);
}
function showBlock(
isMe: boolean | undefined,
isPrivate: boolean | undefined
): boolean {
return Boolean(!isMe) && Boolean(isPrivate);
}
function showClearNickname(
isPublic: boolean | undefined,
isRss: boolean | undefined,
isMe: boolean | undefined,
hasNickname: boolean | undefined
): boolean {
return (
Boolean(!isPublic) &&
Boolean(!isRss) &&
Boolean(!isMe) &&
Boolean(hasNickname)
);
}
function showCopyId(
isPublic: boolean | undefined,
isRss: boolean | undefined
): boolean {
return Boolean(!isPublic) && Boolean(!isRss);
}
function showDeleteContact(
isMe: boolean | undefined,
isClosable: boolean | undefined,
isGroup: boolean | undefined,
isPublic: boolean | undefined,
isRss: boolean | undefined
): boolean {
return (
Boolean(!isMe) && Boolean(isClosable) && !!(!isGroup || isPublic || isRss)
);
}
function showAddModerators(
amMod: boolean | undefined,
isKickedFromGroup: boolean | undefined
): boolean {
return Boolean(!isKickedFromGroup) && Boolean(amMod);
}
function showRemoveModerators(
amMod: boolean | undefined,
isKickedFromGroup: boolean | undefined
): boolean {
return Boolean(!isKickedFromGroup) && Boolean(amMod);
}
function showUpdateGroupName(
amMod: boolean | undefined,
isKickedFromGroup: boolean | undefined
): boolean {
return Boolean(!isKickedFromGroup) && Boolean(amMod);
}
function showLeaveGroup(
isKickedFromGroup: boolean | undefined,
isGroup: boolean | undefined,
isPublic: boolean | undefined,
isRss: boolean | undefined
): boolean {
return Boolean(!isKickedFromGroup) && Boolean(isGroup) && !isPublic && !isRss;
}
function showInviteContact(
isGroup: boolean | undefined,
isPublic: boolean | undefined
): boolean {
return Boolean(isGroup) && Boolean(isPublic);
}
/** Menu items standardized */
export function getInviteContactMenuItem(
isGroup: boolean | undefined,
isPublic: boolean | undefined,
action: any,
i18n: LocalizerType
): JSX.Element | null {
if (showInviteContact(isGroup, isPublic)) {
return <MenuItem onClick={action}>{i18n('inviteContacts')}</MenuItem>;
}
return null;
}
export function getDeleteContactMenuItem(
isMe: boolean | undefined,
isClosable: boolean | undefined,
isGroup: boolean | undefined,
isPublic: boolean | undefined,
isRss: boolean | undefined,
action: any,
i18n: LocalizerType
): JSX.Element | null {
if (showDeleteContact(isMe, isClosable, isGroup, isPublic, isRss)) {
if (isPublic) {
return (
<MenuItem onClick={action}>{i18n('deletePublicChannel')}</MenuItem>
);
}
return <MenuItem onClick={action}>{i18n('deleteContact')}</MenuItem>;
}
return null;
}
export function getLeaveGroupMenuItem(
isKickedFromGroup: boolean | undefined,
isGroup: boolean | undefined,
isPublic: boolean | undefined,
isRss: boolean | undefined,
action: any,
i18n: LocalizerType
): JSX.Element | null {
if (showLeaveGroup(isKickedFromGroup, isGroup, isPublic, isRss)) {
return <MenuItem onClick={action}>{i18n('leaveGroup')}</MenuItem>;
}
return null;
}
export function getUpdateGroupNameMenuItem(
amMod: boolean | undefined,
isKickedFromGroup: boolean | undefined,
action: any,
i18n: LocalizerType
): JSX.Element | null {
if (showUpdateGroupName(amMod, isKickedFromGroup)) {
return (
<MenuItem onClick={action}>{i18n('editGroupNameOrPicture')}</MenuItem>
);
}
return null;
}
export function getRemoveModeratorsMenuItem(
amMod: boolean | undefined,
isKickedFromGroup: boolean | undefined,
action: any,
i18n: LocalizerType
): JSX.Element | null {
if (showRemoveModerators(amMod, isKickedFromGroup)) {
return <MenuItem onClick={action}>{i18n('removeModerators')}</MenuItem>;
}
return null;
}
export function getAddModeratorsMenuItem(
amMod: boolean | undefined,
isKickedFromGroup: boolean | undefined,
action: any,
i18n: LocalizerType
): JSX.Element | null {
if (showAddModerators(amMod, isKickedFromGroup)) {
return <MenuItem onClick={action}>{i18n('addModerators')}</MenuItem>;
}
return null;
}
export function getCopyIdMenuItem(
isPublic: boolean | undefined,
isRss: boolean | undefined,
isGroup: boolean | undefined,
action: any,
i18n: LocalizerType
): JSX.Element | null {
if (showCopyId(isPublic, isRss)) {
const copyIdLabel = isGroup ? i18n('copyChatId') : i18n('copyPublicKey');
return <MenuItem onClick={action}>{copyIdLabel}</MenuItem>;
}
return null;
}
export function getDisappearingMenuItem(
isPublic: boolean | undefined,
isRss: boolean | undefined,
isKickedFromGroup: boolean | undefined,
isBlocked: boolean | undefined,
timerOptions: Array<TimerOption>,
action: any,
i18n: LocalizerType
): JSX.Element | null {
if (
showTimerOptions(
Boolean(isPublic),
Boolean(isRss),
Boolean(isKickedFromGroup),
Boolean(isBlocked)
)
) {
return (
<SubMenu title={i18n('disappearingMessages') as any}>
{(timerOptions || []).map(item => (
<MenuItem
key={item.value}
onClick={() => {
action(item.value);
}}
>
{item.name}
</MenuItem>
))}
</SubMenu>
);
}
return null;
}
export function getShowMemberMenuItem(
isPublic: boolean | undefined,
isRss: boolean | undefined,
isGroup: boolean | undefined,
action: any,
i18n: LocalizerType
): JSX.Element | null {
if (showMemberMenu(Boolean(isPublic), Boolean(isRss), Boolean(isGroup))) {
return <MenuItem onClick={action}>{i18n('showMembers')}</MenuItem>;
}
return null;
}
export function getShowSafetyNumberMenuItem(
isPublic: boolean | undefined,
isRss: boolean | undefined,
isGroup: boolean | undefined,
isMe: boolean | undefined,
action: any,
i18n: LocalizerType
): JSX.Element | null {
if (
showSafetyNumber(
Boolean(isPublic),
Boolean(isRss),
Boolean(isGroup),
Boolean(isMe)
)
) {
return <MenuItem onClick={action}>{i18n('showSafetyNumber')}</MenuItem>;
}
return null;
}
export function getResetSessionMenuItem(
isPublic: boolean | undefined,
isRss: boolean | undefined,
isGroup: boolean | undefined,
action: any,
i18n: LocalizerType
): JSX.Element | null {
if (showResetSession(Boolean(isPublic), Boolean(isRss), Boolean(isGroup))) {
return <MenuItem onClick={action}>{i18n('resetSession')}</MenuItem>;
}
return null;
}
export function getBlockMenuItem(
isMe: boolean | undefined,
isPrivate: boolean | undefined,
isBlocked: boolean | undefined,
actionBlock: any,
actionUnblock: any,
i18n: LocalizerType
): JSX.Element | null {
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 null;
}
export function getClearNicknameMenuItem(
isPublic: boolean | undefined,
isRss: boolean | undefined,
isMe: boolean | undefined,
hasNickname: boolean | undefined,
action: any,
i18n: LocalizerType
): JSX.Element | null {
if (
showClearNickname(
Boolean(isPublic),
Boolean(isRss),
Boolean(isMe),
Boolean(hasNickname)
)
) {
return <MenuItem onClick={action}>{i18n('clearNickname')}</MenuItem>;
}
return null;
}