From 6cf838ade8cbd587a552b53e003c56c665b14dab Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Wed, 13 Jan 2021 12:36:31 +1100 Subject: [PATCH] disallow remove of an admin from a closed group --- _locales/en/messages.json | 8 +++++ js/models/conversations.js | 2 -- js/views/create_group_dialog_view.js | 2 ++ .../conversation/InviteContactsDialog.tsx | 5 ++- .../conversation/UpdateGroupMembersDialog.tsx | 35 +++++++++---------- .../session/SessionClosableOverlay.tsx | 4 +-- .../session/SessionMemberListItem.tsx | 22 ++---------- ts/session/utils/Toast.tsx | 8 +++++ .../integration/stubs/stub_message_api.ts | 3 +- 9 files changed, 45 insertions(+), 44 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 72d1fce70..136851103 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1748,6 +1748,14 @@ "message": "As you are the admin of this group, if you leave it it will be removed for every current members. Are you sure you want to leave this group?", "description": "Confirmation dialog text that tells the user what will happen if they leave the group as admin." }, + "cannotRemoveCreatorFromGroup": { + "message": "Cannot remove this user", + "description": "Toast title when the user tries to remove the creator from a closed group v2 member list." + }, + "cannotRemoveCreatorFromGroupDesc": { + "message": "You cannot remove this user as he is the creator of the group.", + "description": "Toast description when the user tries to remove the creator from a closed group v2 member list." + }, "noContactsForGroup": { "message": "You don't have any contacts yet", "androidKey": "activity_create_closed_group_empty_state_message" diff --git a/js/models/conversations.js b/js/models/conversations.js index 15744dbd7..96e713890 100644 --- a/js/models/conversations.js +++ b/js/models/conversations.js @@ -34,8 +34,6 @@ upgradeMessageSchema, loadAttachmentData, getAbsoluteAttachmentPath, - // eslint-disable-next-line no-unused-vars - writeNewAttachmentData, deleteAttachmentData, } = window.Signal.Migrations; diff --git a/js/views/create_group_dialog_view.js b/js/views/create_group_dialog_view.js index 06e0641e9..7d66a3e0a 100644 --- a/js/views/create_group_dialog_view.js +++ b/js/views/create_group_dialog_view.js @@ -105,6 +105,7 @@ this.isAdmin = groupConvo.isMediumGroup() ? true : groupConvo.get('groupAdmins').includes(ourPK); + this.admins = groupConvo.get('groupAdmins'); const convos = window .getConversationController() .getConversations() @@ -142,6 +143,7 @@ existingMembers: this.existingMembers, contactList: this.contactsAndMembers, isAdmin: this.isAdmin, + admins: this.admins, onClose: this.close, onSubmit: this.onSubmit, groupId: this.groupId, diff --git a/ts/components/conversation/InviteContactsDialog.tsx b/ts/components/conversation/InviteContactsDialog.tsx index 6055568aa..1ee98fe72 100644 --- a/ts/components/conversation/InviteContactsDialog.tsx +++ b/ts/components/conversation/InviteContactsDialog.tsx @@ -113,13 +113,16 @@ class InviteContactsDialogInner extends React.Component { private renderMemberList() { const members = this.state.contactList; + const selectedContacts = this.state.contactList + .filter(d => d.checkmarked) + .map(d => d.id); return members.map((member: ContactType, index: number) => ( m === member.id)} onSelect={(selectedMember: ContactType) => { this.onMemberClicked(selectedMember); }} diff --git a/ts/components/conversation/UpdateGroupMembersDialog.tsx b/ts/components/conversation/UpdateGroupMembersDialog.tsx index 29e3d7bb7..794588f75 100644 --- a/ts/components/conversation/UpdateGroupMembersDialog.tsx +++ b/ts/components/conversation/UpdateGroupMembersDialog.tsx @@ -9,6 +9,7 @@ import { SessionMemberListItem, } from '../session/SessionMemberListItem'; import { DefaultTheme } from 'styled-components'; +import { ToastUtils } from '../../session/utils'; interface Props { titleText: string; @@ -19,6 +20,8 @@ interface Props { contactList: Array; isAdmin: boolean; existingMembers: Array; + admins: Array; // used for closed group v2 + i18n: any; onSubmit: any; onClose: any; @@ -165,23 +168,6 @@ export class UpdateGroupMembersDialog extends React.Component { )); } - private onShowError(msg: string) { - if (this.state.errorDisplayed) { - return; - } - - this.setState({ - errorDisplayed: true, - errorMessage: msg, - }); - - setTimeout(() => { - this.setState({ - errorDisplayed: false, - }); - }, 3000); - } - private onKeyUp(event: any) { switch (event.key) { case 'Enter': @@ -218,12 +204,23 @@ export class UpdateGroupMembersDialog extends React.Component { } private onMemberClicked(selected: any) { - if (selected.existingMember && !this.props.isAdmin) { + const { isAdmin, admins } = this.props; + const { contactList } = this.state; + + if (selected.existingMember && !isAdmin) { window.log.warn('Only group admin can remove members!'); return; } - const updatedContacts = this.state.contactList.map(member => { + if (selected.existingMember && admins.includes(selected.id)) { + window.log.warn( + `User ${selected.id} cannot be removed as he is the creator of this closed group v2.` + ); + ToastUtils.pushCannotRemoveCreatorFromGroup(); + return; + } + + const updatedContacts = contactList.map(member => { if (member.id === selected.id) { return { ...member, checkmarked: !member.checkmarked }; } else { diff --git a/ts/components/session/SessionClosableOverlay.tsx b/ts/components/session/SessionClosableOverlay.tsx index 8931d601a..2b9bd03db 100644 --- a/ts/components/session/SessionClosableOverlay.tsx +++ b/ts/components/session/SessionClosableOverlay.tsx @@ -13,7 +13,6 @@ import { } from './SessionButton'; import { SessionSpinner } from './SessionSpinner'; import { PillDivider } from './PillDivider'; -import classNames from 'classnames'; import { DefaultTheme } from 'styled-components'; export enum SessionClosableOverlayType { @@ -274,10 +273,11 @@ export class SessionClosableOverlay extends React.Component { private renderMemberList(members: any) { return members.map((member: ContactType, index: number) => ( + // tslint:disable-next-line: use-simple-attributes m.id === member.id)} key={member.id} onSelect={(selectedMember: ContactType) => { this.handleSelectMember(selectedMember); diff --git a/ts/components/session/SessionMemberListItem.tsx b/ts/components/session/SessionMemberListItem.tsx index 3862b6b20..101ed1aa2 100644 --- a/ts/components/session/SessionMemberListItem.tsx +++ b/ts/components/session/SessionMemberListItem.tsx @@ -26,11 +26,7 @@ interface Props { theme: DefaultTheme; } -interface State { - isSelected: boolean; -} - -class SessionMemberListItemInner extends React.Component { +class SessionMemberListItemInner extends React.Component { public static defaultProps = { isSelected: false, }; @@ -38,10 +34,6 @@ class SessionMemberListItemInner extends React.Component { constructor(props: any) { super(props); - this.state = { - isSelected: this.props.isSelected, - }; - this.handleSelectionAction = this.handleSelectionAction.bind(this); this.selectMember = this.selectMember.bind(this); this.unselectMember = this.unselectMember.bind(this); @@ -49,7 +41,7 @@ class SessionMemberListItemInner extends React.Component { } public render() { - const { isSelected } = this.state; + const { isSelected } = this.props; const name = this.props.member.authorProfileName; @@ -105,7 +97,7 @@ class SessionMemberListItemInner extends React.Component { } private handleSelectionAction() { - if (this.state.isSelected) { + if (this.props.isSelected) { this.unselectMember(); return; @@ -115,20 +107,12 @@ class SessionMemberListItemInner extends React.Component { } private selectMember() { - this.setState({ - isSelected: true, - }); - if (this.props.onSelect) { this.props.onSelect(this.props.member); } } private unselectMember() { - this.setState({ - isSelected: false, - }); - if (this.props.onUnselect) { this.props.onUnselect(this.props.member); } diff --git a/ts/session/utils/Toast.tsx b/ts/session/utils/Toast.tsx index d88611387..4ce058f5a 100644 --- a/ts/session/utils/Toast.tsx +++ b/ts/session/utils/Toast.tsx @@ -251,3 +251,11 @@ export function pushDeleted() { SessionIconType.Check ); } + +export function pushCannotRemoveCreatorFromGroup() { + pushToastWarning( + 'cannotRemoveCreatorFromGroup', + window.i18n('cannotRemoveCreatorFromGroup'), + window.i18n('cannotRemoveCreatorFromGroupDesc') + ); +} diff --git a/ts/test/session/integration/stubs/stub_message_api.ts b/ts/test/session/integration/stubs/stub_message_api.ts index 0bb7d718d..30aa72b56 100644 --- a/ts/test/session/integration/stubs/stub_message_api.ts +++ b/ts/test/session/integration/stubs/stub_message_api.ts @@ -24,7 +24,8 @@ class StubMessageAPI { const data64 = StringUtils.decode(data, 'base64'); await fetch( - `${this.baseUrl + `${ + this.baseUrl }/messages?pubkey=${pubKey}×tamp=${messageTimeStamp}&data=${encodeURIComponent( data64 )}`,