remove zombies from the UI only

pull/1592/head
Audric Ackermann 4 years ago
parent 2c40d1e623
commit 64eab5160d
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4

@ -2056,6 +2056,10 @@
"message": "Anonymous", "message": "Anonymous",
"description": "The name of currently unidentified users" "description": "The name of currently unidentified users"
}, },
"removeResidueMembers": {
"message": "Clicking ok will also remove those members as they left the group.",
"description": "Shown when the admin shows the group member dialog, to warn him that zombies members will be removed"
},
"enterDisplayName": { "enterDisplayName": {
"message": "Enter a display name", "message": "Enter a display name",
"androidKey": "activity_display_name_edit_text_hint" "androidKey": "activity_display_name_edit_text_hint"

@ -1,14 +1,21 @@
import React from 'react'; import React from 'react';
import styled from 'styled-components'; import styled, { DefaultTheme } from 'styled-components';
type TextProps = { type TextProps = {
text: string; text: string;
subtle?: boolean; subtle?: boolean;
opposite?: boolean; opposite?: boolean;
maxWidth?: string;
padding?: string;
textAlign?: 'center';
theme?: DefaultTheme;
}; };
const StyledDefaultText = styled.div<TextProps>` const StyledDefaultText = styled.div<TextProps>`
transition: ${props => props.theme.common.animations.defaultDuration}; transition: ${props => props.theme.common.animations.defaultDuration};
max-width: ${props => (props.maxWidth ? props.maxWidth : '')};
padding: ${props => (props.padding ? props.padding : '')};
text-align: ${props => (props.textAlign ? props.textAlign : '')};
font-family: ${props => props.theme.common.fonts.sessionFontDefault}; font-family: ${props => props.theme.common.fonts.sessionFontDefault};
color: ${props => color: ${props =>
props.opposite props.opposite

@ -143,11 +143,16 @@ class InviteContactsDialogInner extends React.Component<Props, State> {
existingMembers = []; existingMembers = [];
} }
existingMembers = _.compact(existingMembers); existingMembers = _.compact(existingMembers);
const existingZombies = convo.get('zombies') || [];
const newMembers = pubkeys.filter(d => !existingMembers.includes(d)); const newMembers = pubkeys.filter(d => !existingMembers.includes(d));
if (newMembers.length > 0) { if (newMembers.length > 0) {
// Do not trigger an update if there is too many members // Do not trigger an update if there is too many members
if (newMembers.length + existingMembers.length > window.CONSTANTS.CLOSED_GROUP_SIZE_LIMIT) { // be sure to include current zombies in this count
if (
newMembers.length + existingMembers.length + existingZombies.length >
window.CONSTANTS.CLOSED_GROUP_SIZE_LIMIT
) {
ToastUtils.pushTooManyMembers(); ToastUtils.pushTooManyMembers();
return; return;
} }

@ -2,7 +2,7 @@ import React from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { SessionModal } from '../session/SessionModal'; import { SessionModal } from '../session/SessionModal';
import { SessionButton, SessionButtonColor } from '../session/SessionButton'; import { SessionButton, SessionButtonColor, SessionButtonType } from '../session/SessionButton';
import { ContactType, SessionMemberListItem } from '../session/SessionMemberListItem'; import { ContactType, SessionMemberListItem } from '../session/SessionMemberListItem';
import { DefaultTheme } from 'styled-components'; import { DefaultTheme } from 'styled-components';
import { ToastUtils } from '../../session/utils'; import { ToastUtils } from '../../session/utils';
@ -11,6 +11,7 @@ import autoBind from 'auto-bind';
import { ConversationController } from '../../session/conversations'; import { ConversationController } from '../../session/conversations';
import _ from 'lodash'; import _ from 'lodash';
import { Text } from '../basic/Text';
interface Props { interface Props {
titleText: string; titleText: string;
@ -25,8 +26,8 @@ interface Props {
admins: Array<string>; // used for closed group admins: Array<string>; // used for closed group
i18n: LocalizerType; i18n: LocalizerType;
onSubmit: any; onSubmit: (membersLeft: Array<string>) => void;
onClose: any; onClose: () => void;
theme: DefaultTheme; theme: DefaultTheme;
} }
@ -106,7 +107,7 @@ export class UpdateGroupMembersDialog extends React.Component<Props, State> {
} }
public render() { public render() {
const { okText, cancelText, isAdmin, contactList, titleText } = this.props; const { okText, cancelText, isAdmin, contactList, titleText, existingZombies } = this.props;
const showNoMembersMessage = contactList.length === 0; const showNoMembersMessage = contactList.length === 0;
@ -116,6 +117,8 @@ export class UpdateGroupMembersDialog extends React.Component<Props, State> {
this.state.errorDisplayed ? 'error-shown' : 'error-faded' this.state.errorDisplayed ? 'error-shown' : 'error-faded'
); );
const hasZombies = Boolean(existingZombies.length);
return ( return (
<SessionModal <SessionModal
title={titleText} title={titleText}
@ -169,8 +172,15 @@ export class UpdateGroupMembersDialog extends React.Component<Props, State> {
} }
private renderZombiesList() { private renderZombiesList() {
return this.state.zombies.map((member: ContactType, index: number) => { const { isAdmin } = this.props;
const isSelected = this.props.isAdmin && !member.checkmarked; const { zombies } = this.state;
if (!zombies.length) {
return <></>;
}
const zombieElements = zombies.map((member: ContactType, index: number) => {
const isSelected = isAdmin && !member.checkmarked;
return ( return (
<SessionMemberListItem <SessionMemberListItem
member={member} member={member}
@ -184,6 +194,22 @@ export class UpdateGroupMembersDialog extends React.Component<Props, State> {
/> />
); );
}); });
return (
<>
<div className="spacer-lg" />
{isAdmin && (
<Text
padding="20px"
theme={this.props.theme}
text={window.i18n('removeResidueMembers')}
subtle={true}
maxWidth="400px"
textAlign="center"
/>
)}
{zombieElements}
</>
);
} }
private onKeyUp(event: any) { private onKeyUp(event: any) {
@ -252,39 +278,11 @@ export class UpdateGroupMembersDialog extends React.Component<Props, State> {
} }
private onZombieClicked(selected: ContactType) { private onZombieClicked(selected: ContactType) {
const { isAdmin, admins } = this.props; const { isAdmin } = this.props;
const { zombies } = this.state;
if (!isAdmin) { if (!isAdmin) {
ToastUtils.pushOnlyAdminCanRemove(); ToastUtils.pushOnlyAdminCanRemove();
return; return;
} }
if (selected.existingMember && !isAdmin) {
window.log.warn('Only group admin can remove members!');
return;
}
if (selected.existingMember && admins.includes(selected.id)) {
window.log.warn(
`User ${selected.id} cannot be removed as they are the creator of the closed group.`
);
ToastUtils.pushCannotRemoveCreatorFromGroup();
return;
}
const updatedZombies = zombies.map(zombie => {
if (zombie.id === selected.id) {
return { ...zombie, checkmarked: !zombie.checkmarked };
} else {
return zombie;
}
});
this.setState(state => {
return {
...state,
zombies: updatedZombies,
};
});
} }
} }

@ -752,18 +752,11 @@ async function handleClosedGroupMemberLeft(envelope: EnvelopePlus, convo: Conver
await ClosedGroup.addUpdateMessage(convo, groupDiff, 'incoming', _.toNumber(envelope.timestamp)); await ClosedGroup.addUpdateMessage(convo, groupDiff, 'incoming', _.toNumber(envelope.timestamp));
convo.updateLastMessage(); convo.updateLastMessage();
// if a user just left and we are the admin, we remove him right away for everyone by sending a MEMBERS_REMOVED message so no need to add him as a zombie // if a user just left and we are the admin, we remove him right away for everyone by sending a MEMBERS_REMOVED message so no need to add him as a zombie
if (!isCurrentUserAdmin && oldMembers.includes(sender)) { if (oldMembers.includes(sender)) {
addMemberToZombies(envelope, PubKey.cast(sender), convo); addMemberToZombies(envelope, PubKey.cast(sender), convo);
} }
convo.set('members', newMembers); convo.set('members', newMembers);
// if we are the admin, and there are still some members after the member left, we send a new keypair
// to the remaining members.
// also if we are the admin, we can tell to everyone that this user is effectively removed
if (isCurrentUserAdmin && !!newMembers.length) {
await ClosedGroup.sendRemovedMembers(convo, [sender], newMembers);
}
await convo.commit(); await convo.commit();
await removeFromCache(envelope); await removeFromCache(envelope);

@ -81,8 +81,6 @@ export async function getGroupSecretKey(groupId: string): Promise<Uint8Array> {
return new Uint8Array(fromHex(secretKey)); return new Uint8Array(fromHex(secretKey));
} }
// tslint:disable: max-func-body-length
// tslint:disable: cyclomatic-complexity
export async function initiateGroupUpdate( export async function initiateGroupUpdate(
groupId: string, groupId: string,
groupName: string, groupName: string,
@ -109,7 +107,6 @@ export async function initiateGroupUpdate(
throw new Error('Legacy group are not supported anymore.'); throw new Error('Legacy group are not supported anymore.');
} }
const oldZombies = convo.get('zombies'); const oldZombies = convo.get('zombies');
console.warn('initiategroupUpdate old zombies:', oldZombies);
// do not give an admins field here. We don't want to be able to update admins and // do not give an admins field here. We don't want to be able to update admins and
// updateOrCreateClosedGroup() will update them if given the choice. // updateOrCreateClosedGroup() will update them if given the choice.
@ -123,7 +120,6 @@ export async function initiateGroupUpdate(
expireTimer: convo.get('expireTimer'), expireTimer: convo.get('expireTimer'),
avatar, avatar,
}; };
console.warn('initiategroupUpdate new zombies:', groupDetails.zombies);
const diff = buildGroupDiff(convo, groupDetails); const diff = buildGroupDiff(convo, groupDetails);
@ -207,7 +203,7 @@ export async function addUpdateMessage(
return message; return message;
} }
export function buildGroupDiff(convo: ConversationModel, update: UpdatableGroupState): GroupDiff { function buildGroupDiff(convo: ConversationModel, update: GroupInfo): GroupDiff {
const groupDiff: GroupDiff = {}; const groupDiff: GroupDiff = {};
if (convo.get('name') !== update.name) { if (convo.get('name') !== update.name) {
@ -215,13 +211,17 @@ export function buildGroupDiff(convo: ConversationModel, update: UpdatableGroupS
} }
const oldMembers = convo.get('members'); const oldMembers = convo.get('members');
const oldZombies = convo.get('zombies');
const oldMembersWithZombies = _.uniq(oldMembers.concat(oldZombies));
const newMembersWithZombiesLeft = _.uniq(update.members.concat(update.zombies || []));
const addedMembers = _.difference(update.members, oldMembers); const addedMembers = _.difference(newMembersWithZombiesLeft, oldMembersWithZombies);
if (addedMembers.length > 0) { if (addedMembers.length > 0) {
groupDiff.joiningMembers = addedMembers; groupDiff.joiningMembers = addedMembers;
} }
// Check if anyone got kicked: // Check if anyone got kicked:
const removedMembers = _.difference(oldMembers, update.members); const removedMembers = _.difference(oldMembersWithZombies, newMembersWithZombiesLeft);
if (removedMembers.length > 0) { if (removedMembers.length > 0) {
groupDiff.leavingMembers = removedMembers; groupDiff.leavingMembers = removedMembers;
} }

Loading…
Cancel
Save