|
|
|
@ -6,6 +6,11 @@ import { SessionButton, SessionButtonColor } from '../session/SessionButton';
|
|
|
|
|
import { ContactType, SessionMemberListItem } from '../session/SessionMemberListItem';
|
|
|
|
|
import { DefaultTheme } from 'styled-components';
|
|
|
|
|
import { ToastUtils } from '../../session/utils';
|
|
|
|
|
import { LocalizerType } from '../../types/Util';
|
|
|
|
|
import autoBind from 'auto-bind';
|
|
|
|
|
import { ConversationController } from '../../session/conversations';
|
|
|
|
|
|
|
|
|
|
import _ from 'lodash';
|
|
|
|
|
|
|
|
|
|
interface Props {
|
|
|
|
|
titleText: string;
|
|
|
|
@ -15,10 +20,11 @@ interface Props {
|
|
|
|
|
// contacts not in the group
|
|
|
|
|
contactList: Array<any>;
|
|
|
|
|
isAdmin: boolean;
|
|
|
|
|
existingMembers: Array<String>;
|
|
|
|
|
admins: Array<String>; // used for closed group
|
|
|
|
|
existingMembers: Array<string>;
|
|
|
|
|
existingZombies: Array<string>;
|
|
|
|
|
admins: Array<string>; // used for closed group
|
|
|
|
|
|
|
|
|
|
i18n: any;
|
|
|
|
|
i18n: LocalizerType;
|
|
|
|
|
onSubmit: any;
|
|
|
|
|
onClose: any;
|
|
|
|
|
theme: DefaultTheme;
|
|
|
|
@ -26,23 +32,21 @@ interface Props {
|
|
|
|
|
|
|
|
|
|
interface State {
|
|
|
|
|
contactList: Array<ContactType>;
|
|
|
|
|
zombies: Array<ContactType>;
|
|
|
|
|
errorDisplayed: boolean;
|
|
|
|
|
errorMessage: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export class UpdateGroupMembersDialog extends React.Component<Props, State> {
|
|
|
|
|
constructor(props: any) {
|
|
|
|
|
constructor(props: Props) {
|
|
|
|
|
super(props);
|
|
|
|
|
|
|
|
|
|
this.onMemberClicked = this.onMemberClicked.bind(this);
|
|
|
|
|
this.onClickOK = this.onClickOK.bind(this);
|
|
|
|
|
this.onKeyUp = this.onKeyUp.bind(this);
|
|
|
|
|
this.closeDialog = this.closeDialog.bind(this);
|
|
|
|
|
autoBind(this);
|
|
|
|
|
|
|
|
|
|
let contacts = this.props.contactList;
|
|
|
|
|
contacts = contacts.map(d => {
|
|
|
|
|
const lokiProfile = d.getLokiProfile();
|
|
|
|
|
const name = lokiProfile ? lokiProfile.displayName : 'Anonymous';
|
|
|
|
|
const name = lokiProfile ? lokiProfile.displayName : window.i18n('anonymous');
|
|
|
|
|
|
|
|
|
|
const existingMember = this.props.existingMembers.includes(d.id);
|
|
|
|
|
|
|
|
|
@ -58,8 +62,33 @@ export class UpdateGroupMembersDialog extends React.Component<Props, State> {
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const zombies = _.compact(
|
|
|
|
|
this.props.existingZombies.map(d => {
|
|
|
|
|
const convo = ConversationController.getInstance().get(d);
|
|
|
|
|
if (!convo) {
|
|
|
|
|
window.log.warn('Zombie convo not found');
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
const lokiProfile = convo.getLokiProfile();
|
|
|
|
|
const name = lokiProfile ? `${lokiProfile.displayName} (Zombie)` : window.i18n('anonymous');
|
|
|
|
|
|
|
|
|
|
const existingZombie = this.props.existingZombies.includes(convo.id);
|
|
|
|
|
return {
|
|
|
|
|
id: convo.id,
|
|
|
|
|
authorPhoneNumber: convo.id,
|
|
|
|
|
authorProfileName: name,
|
|
|
|
|
authorAvatarPath: convo?.getAvatarPath() as string,
|
|
|
|
|
selected: false,
|
|
|
|
|
authorName: name,
|
|
|
|
|
checkmarked: false,
|
|
|
|
|
existingMember: existingZombie,
|
|
|
|
|
};
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
this.state = {
|
|
|
|
|
contactList: contacts,
|
|
|
|
|
zombies,
|
|
|
|
|
errorDisplayed: false,
|
|
|
|
|
errorMessage: '',
|
|
|
|
|
};
|
|
|
|
@ -70,13 +99,14 @@ export class UpdateGroupMembersDialog extends React.Component<Props, State> {
|
|
|
|
|
public onClickOK() {
|
|
|
|
|
const members = this.getWouldBeMembers(this.state.contactList).map(d => d.id);
|
|
|
|
|
|
|
|
|
|
// do not include zombies here, they are removed by force
|
|
|
|
|
this.props.onSubmit(members);
|
|
|
|
|
|
|
|
|
|
this.closeDialog();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public render() {
|
|
|
|
|
const { okText, cancelText, contactList, titleText } = this.props;
|
|
|
|
|
const { okText, cancelText, isAdmin, contactList, titleText } = this.props;
|
|
|
|
|
|
|
|
|
|
const showNoMembersMessage = contactList.length === 0;
|
|
|
|
|
|
|
|
|
@ -99,17 +129,20 @@ export class UpdateGroupMembersDialog extends React.Component<Props, State> {
|
|
|
|
|
<div className="spacer-md" />
|
|
|
|
|
|
|
|
|
|
<div className="group-member-list__selection">{this.renderMemberList()}</div>
|
|
|
|
|
{this.renderZombiesList()}
|
|
|
|
|
{showNoMembersMessage && <p>{window.i18n('noMembersInThisGroup')}</p>}
|
|
|
|
|
|
|
|
|
|
<div className="spacer-lg" />
|
|
|
|
|
|
|
|
|
|
<div className="session-modal__button-group">
|
|
|
|
|
<SessionButton text={cancelText} onClick={this.closeDialog} />
|
|
|
|
|
<SessionButton
|
|
|
|
|
text={okText}
|
|
|
|
|
onClick={this.onClickOK}
|
|
|
|
|
buttonColor={SessionButtonColor.Green}
|
|
|
|
|
/>
|
|
|
|
|
{isAdmin && (
|
|
|
|
|
<SessionButton
|
|
|
|
|
text={okText}
|
|
|
|
|
onClick={this.onClickOK}
|
|
|
|
|
buttonColor={SessionButtonColor.Green}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</SessionModal>
|
|
|
|
|
);
|
|
|
|
@ -131,6 +164,21 @@ export class UpdateGroupMembersDialog extends React.Component<Props, State> {
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private renderZombiesList() {
|
|
|
|
|
return this.state.zombies.map((member: ContactType, index: number) => (
|
|
|
|
|
<SessionMemberListItem
|
|
|
|
|
member={member}
|
|
|
|
|
index={index}
|
|
|
|
|
isSelected={!member.checkmarked}
|
|
|
|
|
onSelect={this.onZombieClicked}
|
|
|
|
|
onUnselect={this.onZombieClicked}
|
|
|
|
|
isZombie={true}
|
|
|
|
|
key={member.id}
|
|
|
|
|
theme={this.props.theme}
|
|
|
|
|
/>
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private onKeyUp(event: any) {
|
|
|
|
|
switch (event.key) {
|
|
|
|
|
case 'Enter':
|
|
|
|
@ -158,10 +206,15 @@ export class UpdateGroupMembersDialog extends React.Component<Props, State> {
|
|
|
|
|
this.props.onClose();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private onMemberClicked(selected: any) {
|
|
|
|
|
private onMemberClicked(selected: ContactType) {
|
|
|
|
|
const { isAdmin, admins } = this.props;
|
|
|
|
|
const { contactList } = this.state;
|
|
|
|
|
|
|
|
|
|
if (!isAdmin) {
|
|
|
|
|
ToastUtils.pushOnlyAdminCanRemove();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (selected.existingMember && !isAdmin) {
|
|
|
|
|
window.log.warn('Only group admin can remove members!');
|
|
|
|
|
return;
|
|
|
|
@ -190,4 +243,41 @@ export class UpdateGroupMembersDialog extends React.Component<Props, State> {
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private onZombieClicked(selected: ContactType) {
|
|
|
|
|
const { isAdmin, admins } = this.props;
|
|
|
|
|
const { zombies } = this.state;
|
|
|
|
|
|
|
|
|
|
if (!isAdmin) {
|
|
|
|
|
ToastUtils.pushOnlyAdminCanRemove();
|
|
|
|
|
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,
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|