|  |  |  | @ -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, | 
		
	
		
			
				|  |  |  |  |       }; | 
		
	
		
			
				|  |  |  |  |     }); | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | } | 
		
	
	
		
			
				
					|  |  |  | 
 |