diff --git a/js/models/conversations.js b/js/models/conversations.js index 7cb29d34c..cb3aa8370 100644 --- a/js/models/conversations.js +++ b/js/models/conversations.js @@ -608,6 +608,7 @@ }, isOnline: this.isOnline(), hasNickname: !!this.getNickname(), + isKickedFromGroup: !!this.get('isKickedFromGroup'), selectedMessages: this.selectedMessages, @@ -620,6 +621,9 @@ onDeleteContact: () => this.deleteContact(), onDeleteMessages: () => this.deleteMessages(), onCloseOverlay: () => this.resetMessageSelection(), + onInviteContacts: () => { + window.Whisper.events.trigger('inviteContacts', this); + }, }; return result; diff --git a/ts/components/ConversationListItem.tsx b/ts/components/ConversationListItem.tsx index 75b48e443..43f4b1c10 100644 --- a/ts/components/ConversationListItem.tsx +++ b/ts/components/ConversationListItem.tsx @@ -12,10 +12,12 @@ import { TypingAnimation } from './conversation/TypingAnimation'; import { Colors, LocalizerType } from '../types/Util'; import { - showClearNickname, - showBlock, - showCopyId, - showDeleteContact, + getBlockMenuItem, + getClearNicknameMenuItem, + getCopyIdMenuItem, + getDeleteContactMenuItem, + getInviteContactMenuItem, + getLeaveGroupMenuItem, } from '../session/utils/Menu'; export type PropsData = { @@ -49,6 +51,7 @@ export type PropsData = { hasNickname?: boolean; isSecondary?: boolean; isGroupInvitation?: boolean; + isKickedFromGroup?: boolean; }; type PropsHousekeeping = { @@ -62,6 +65,7 @@ type PropsHousekeeping = { onClearNickname?: () => void; onCopyPublicKey?: () => void; onUnblockContact?: () => void; + onInviteContacts?: () => void; }; type Props = PropsData & PropsHousekeeping; @@ -170,53 +174,72 @@ export class ConversationListItem extends React.PureComponent { isPublic, hasNickname, type, + isKickedFromGroup, onDeleteContact, onDeleteMessages, onBlockContact, - onChangeNickname, onClearNickname, onCopyPublicKey, onUnblockContact, + onInviteContacts, } = this.props; - const blockTitle = isBlocked ? i18n('unblockUser') : i18n('blockUser'); - const blockHandler = isBlocked ? onUnblockContact : onBlockContact; const isPrivate = type === 'direct'; return ( - {showBlock(isMe, isPrivate) ? ( - {blockTitle} - ) : null} + {getBlockMenuItem( + isMe, + isPrivate, + isBlocked, + onBlockContact, + onUnblockContact, + i18n + )} {/* {!isPublic && !isRss && !isMe ? ( {i18n('changeNickname')} ) : null} */} - {showClearNickname(isPublic, isRss, isMe, hasNickname) ? ( - {i18n('clearNickname')} - ) : null} - {showCopyId(isPublic, isRss) ? ( - {i18n('copyPublicKey')} - ) : null} + {getClearNicknameMenuItem( + isPublic, + isRss, + isMe, + hasNickname, + onClearNickname, + i18n + )} + {getCopyIdMenuItem( + isPublic, + isRss, + type === 'group', + onCopyPublicKey, + i18n + )} {i18n('deleteMessages')} - {showDeleteContact( + {getInviteContactMenuItem( + type === 'group', + isPublic, + onInviteContacts, + i18n + )} + {getDeleteContactMenuItem( isMe, isClosable, type === 'group', isPublic, - isRss - ) ? ( - !isPublic ? ( - - {i18n('deleteContact')} - - ) : ( - - {i18n('deletePublicChannel')} - - ) - ) : null} + isRss, + onDeleteContact, + i18n + )} + {getLeaveGroupMenuItem( + isKickedFromGroup, + type === 'group', + isPublic, + isRss, + onDeleteContact, + i18n + )} ); } diff --git a/ts/components/conversation/ConversationHeader.tsx b/ts/components/conversation/ConversationHeader.tsx index 0e530554d..69355ea29 100644 --- a/ts/components/conversation/ConversationHeader.tsx +++ b/ts/components/conversation/ConversationHeader.tsx @@ -20,20 +20,7 @@ import { SessionButtonColor, SessionButtonType, } from '../session/SessionButton'; -import { - showAddModerators, - showBlock, - showCopyId, - showDeleteContact, - showInviteContact, - showLeaveGroup, - showMemberMenu, - showRemoveModerators, - showResetSession, - showSafetyNumber, - showTimerOptions, - showUpdateGroupName, -} from '../../session/utils/Menu'; +import * as Menu from '../../session/utils/Menu'; export interface TimerOption { name: string; @@ -319,48 +306,59 @@ export class ConversationHeader extends React.Component { onUpdateGroupName, } = this.props; - const copyIdLabel = isGroup ? i18n('copyChatId') : i18n('copyPublicKey'); - return ( {this.renderPublicMenuItems()} - {showCopyId(isPublic, isRss) ? ( - {copyIdLabel} - ) : null} + {Menu.getCopyIdMenuItem( + isPublic, + isRss, + isGroup, + onCopyPublicKey, + i18n + )} {i18n('deleteMessages')} - {showAddModerators(amMod, isKickedFromGroup) ? ( - {i18n('addModerators')} - ) : null} - {showRemoveModerators(amMod, isKickedFromGroup) ? ( - - {i18n('removeModerators')} - - ) : null} - {showUpdateGroupName(amMod, isKickedFromGroup) ? ( - - {i18n('editGroupNameOrPicture')} - - ) : null} - {showLeaveGroup(isKickedFromGroup, isGroup, isPublic, isRss) ? ( - {i18n('leaveGroup')} - ) : null} + {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 */} - {showInviteContact(isGroup, isPublic) ? ( - - {i18n('inviteContacts')} - - ) : null} - {showDeleteContact(isMe, isClosable, isGroup, isPublic, isRss) ? ( - !isPublic ? ( - - {i18n('deleteContact')} - - ) : ( - - {i18n('deletePublicChannel')} - - ) - ) : null} + {Menu.getInviteContactMenuItem( + isGroup, + isPublic, + onInviteContacts, + i18n + )} + {Menu.getDeleteContactMenuItem( + isMe, + isClosable, + isGroup, + isPublic, + isRss, + onDeleteContact, + i18n + )} ); } @@ -459,53 +457,45 @@ export class ConversationHeader extends React.Component { onUnblockUser, } = this.props; - if (isPublic || isRss) { - return null; - } - - const disappearingTitle = i18n('disappearingMessages') as any; - - const blockTitle = isBlocked ? i18n('unblockUser') : i18n('blockUser'); - const blockHandler = isBlocked ? onUnblockUser : onBlockUser; - - const disappearingMessagesMenuItem = showTimerOptions( + const disappearingMessagesMenuItem = Menu.getDisappearingMenuItem( isPublic, isRss, isKickedFromGroup, - isBlocked - ) && ( - - {(timerOptions || []).map(item => ( - { - onSetDisappearingMessages(item.value); - }} - > - {item.name} - - ))} - + isBlocked, + timerOptions, + onSetDisappearingMessages, + i18n ); - const showMembersMenuItem = showMemberMenu(isPublic, isRss, isGroup) && ( - {i18n('showMembers')} - ); - - const showSafetyNumberMenuItem = showSafetyNumber( + const showMembersMenuItem = Menu.getShowMemberMenuItem( isPublic, isRss, isGroup, - isMe - ) && ( - - {i18n('showSafetyNumber')} - + onShowGroupMembers, + i18n ); - const resetSessionMenuItem = showResetSession(isPublic, isRss, isGroup) && ( - {i18n('resetSession')} + + const showSafetyNumberMenuItem = Menu.getShowSafetyNumberMenuItem( + isPublic, + isRss, + isGroup, + isMe, + onShowSafetyNumber, + i18n ); - const blockHandlerMenuItem = showBlock(isMe, isPrivate) && ( - {blockTitle} + const resetSessionMenuItem = Menu.getResetSessionMenuItem( + isPublic, + isRss, + isGroup, + onResetSession, + i18n + ); + const blockHandlerMenuItem = Menu.getBlockMenuItem( + isMe, + isPrivate, + isBlocked, + onBlockUser, + onUnblockUser, + i18n ); return ( diff --git a/ts/session/utils/Menu.ts b/ts/session/utils/Menu.ts deleted file mode 100644 index 63418c5b0..000000000 --- a/ts/session/utils/Menu.ts +++ /dev/null @@ -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); -} diff --git a/ts/session/utils/Menu.tsx b/ts/session/utils/Menu.tsx new file mode 100644 index 000000000..e6c47d372 --- /dev/null +++ b/ts/session/utils/Menu.tsx @@ -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 {i18n('inviteContacts')}; + } + 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 ( + {i18n('deletePublicChannel')} + ); + } + return {i18n('deleteContact')}; + } + 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 {i18n('leaveGroup')}; + } + return null; +} + +export function getUpdateGroupNameMenuItem( + amMod: boolean | undefined, + isKickedFromGroup: boolean | undefined, + action: any, + i18n: LocalizerType +): JSX.Element | null { + if (showUpdateGroupName(amMod, isKickedFromGroup)) { + return ( + {i18n('editGroupNameOrPicture')} + ); + } + return null; +} + +export function getRemoveModeratorsMenuItem( + amMod: boolean | undefined, + isKickedFromGroup: boolean | undefined, + action: any, + i18n: LocalizerType +): JSX.Element | null { + if (showRemoveModerators(amMod, isKickedFromGroup)) { + return {i18n('removeModerators')}; + } + return null; +} + +export function getAddModeratorsMenuItem( + amMod: boolean | undefined, + isKickedFromGroup: boolean | undefined, + action: any, + i18n: LocalizerType +): JSX.Element | null { + if (showAddModerators(amMod, isKickedFromGroup)) { + return {i18n('addModerators')}; + } + 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 {copyIdLabel}; + } + return null; +} + +export function getDisappearingMenuItem( + isPublic: boolean | undefined, + isRss: boolean | undefined, + isKickedFromGroup: boolean | undefined, + isBlocked: boolean | undefined, + timerOptions: Array, + action: any, + i18n: LocalizerType +): JSX.Element | null { + if ( + showTimerOptions( + Boolean(isPublic), + Boolean(isRss), + Boolean(isKickedFromGroup), + Boolean(isBlocked) + ) + ) { + return ( + + {(timerOptions || []).map(item => ( + { + action(item.value); + }} + > + {item.name} + + ))} + + ); + } + 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 {i18n('showMembers')}; + } + 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 {i18n('showSafetyNumber')}; + } + 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 {i18n('resetSession')}; + } + 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 {blockTitle}; + } + 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 {i18n('clearNickname')}; + } + return null; +}