import React from 'react'; import classNames from 'classnames'; import { Contact, MemberList } from './MemberList'; import { SessionModal } from './../session/SessionModal'; declare global { interface Window { SMALL_GROUP_SIZE_LIMIT: number; Lodash: any; } } interface Props { titleText: string; groupName: string; okText: string; cancelText: string; // friends not in the group friendList: Array<any>; isAdmin: boolean; existingMembers: Array<any>; i18n: any; onSubmit: any; onClose: any; } interface State { friendList: Array<Contact>; groupName: string; errorDisplayed: boolean; errorMessage: string; } export class UpdateGroupDialog extends React.Component<Props, State> { constructor(props: any) { 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); this.onGroupNameChanged = this.onGroupNameChanged.bind(this); let friends = this.props.friendList; friends = friends.map(d => { const lokiProfile = d.getLokiProfile(); const name = lokiProfile ? lokiProfile.displayName : 'Anonymous'; const existingMember = this.props.existingMembers.includes(d.id); return { id: d.id, authorPhoneNumber: d.id, authorProfileName: name, selected: false, authorName: name, // different from ProfileName? authorColor: d.getColor(), checkmarked: false, existingMember, }; }); this.state = { friendList: friends, groupName: this.props.groupName, errorDisplayed: false, errorMessage: 'placeholder', }; window.addEventListener('keyup', this.onKeyUp); } public onClickOK() { const members = this.getWouldBeMembers(this.state.friendList).map( d => d.id ); if (!this.state.groupName.trim()) { this.onShowError(this.props.i18n('emptyGroupNameError')); return; } this.props.onSubmit(this.state.groupName, members); this.closeDialog(); } public render() { const checkMarkedCount = this.getMemberCount(this.state.friendList); const titleText = `${this.props.titleText} (Members: ${checkMarkedCount})`; const okText = this.props.okText; const cancelText = this.props.cancelText; const noFriendsClasses = this.state.friendList.length === 0 ? 'no-friends' : classNames('no-friends', 'hidden'); const errorMsg = this.state.errorMessage; const errorMessageClasses = classNames( 'error-message', this.state.errorDisplayed ? 'error-shown' : 'error-faded' ); return ( <SessionModal title={titleText} onClose={() => null} onOk={() => null}> <div className="spacer-md" /> <p className={errorMessageClasses}>{errorMsg}</p> <div className="spacer-md" /> <input type="text" id="group-name" className="group-name" placeholder={this.props.i18n('groupNamePlaceholder')} value={this.state.groupName} disabled={!this.props.isAdmin} onChange={this.onGroupNameChanged} tabIndex={0} required={true} aria-required={true} autoFocus={true} /> <div className="friend-selection-list"> <MemberList members={this.state.friendList} selected={{}} i18n={this.props.i18n} onMemberClicked={this.onMemberClicked} /> </div> <p className={noFriendsClasses}>{`(${this.props.i18n( 'noFriendsToAdd' )})`}</p> <div className="buttons"> <button className="cancel" tabIndex={0} onClick={this.closeDialog}> {cancelText} </button> <button className="ok" tabIndex={0} onClick={this.onClickOK}> {okText} </button> </div> </SessionModal> ); } 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': this.onClickOK(); break; case 'Esc': case 'Escape': this.closeDialog(); break; default: } } // Return members that would comprise the group given the // current state in `users` private getWouldBeMembers(users: Array<Contact>) { return users.filter(d => { return ( (d.existingMember && !d.checkmarked) || (!d.existingMember && d.checkmarked) ); }); } private getMemberCount(users: Array<Contact>) { // Adding one to include ourselves return this.getWouldBeMembers(users).length + 1; } private closeDialog() { window.removeEventListener('keyup', this.onKeyUp); this.props.onClose(); } private onMemberClicked(selected: any) { if (selected.existingMember && !this.props.isAdmin) { this.onShowError(this.props.i18n('nonAdminDeleteMember')); return; } const updatedFriends = this.state.friendList.map(member => { if (member.id === selected.id) { return { ...member, checkmarked: !member.checkmarked }; } else { return member; } }); const newMemberCount = this.getMemberCount(updatedFriends); if (newMemberCount > window.SMALL_GROUP_SIZE_LIMIT) { const msg = `${this.props.i18n('maxGroupMembersError')} ${ window.SMALL_GROUP_SIZE_LIMIT }`; this.onShowError(msg); return; } this.setState(state => { return { ...state, friendList: updatedFriends, }; }); } private onGroupNameChanged(event: any) { event.persist(); this.setState(state => { return { ...state, groupName: event.target.value, }; }); } }