Merge pull request #6535 from KDSBrowne/2.2-add-create-br-to-manage-user-menu

Add create breakout room option to manage user menu
This commit is contained in:
Anton Georgiev 2019-01-28 13:44:15 -05:00 committed by GitHub
commit 7349fcabb5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 203 additions and 119 deletions

View File

@ -2,6 +2,4 @@ import { Meteor } from 'meteor/meteor';
const Streamer = new Meteor.Streamer('videos'); const Streamer = new Meteor.Streamer('videos');
export default Streamer export default Streamer;

View File

@ -145,8 +145,6 @@ class ActionsDropdown extends Component {
pollBtnDesc, pollBtnDesc,
presentationLabel, presentationLabel,
presentationDesc, presentationDesc,
startRecording,
stopRecording,
createBreakoutRoom, createBreakoutRoom,
createBreakoutRoomDesc, createBreakoutRoomDesc,
invitationItem, invitationItem,
@ -158,6 +156,10 @@ class ActionsDropdown extends Component {
formatMessage, formatMessage,
} = intl; } = intl;
const canCreateBreakout = isUserModerator
&& !meetingIsBreakout
&& !hasBreakoutRoom;
const canInviteUsers = isUserModerator const canInviteUsers = isUserModerator
&& !meetingIsBreakout && !meetingIsBreakout
&& hasBreakoutRoom && hasBreakoutRoom
@ -209,12 +211,12 @@ class ActionsDropdown extends Component {
/> />
) )
: null), : null),
(isUserModerator && !meetingIsBreakout && !hasBreakoutRoom (canCreateBreakout
? ( ? (
<DropdownListItem <DropdownListItem
icon="rooms" icon="rooms"
label={intl.formatMessage(intlMessages.createBreakoutRoom)} label={formatMessage(createBreakoutRoom)}
description={intl.formatMessage(intlMessages.createBreakoutRoomDesc)} description={formatMessage(createBreakoutRoomDesc)}
key={this.createBreakoutRoomId} key={this.createBreakoutRoomId}
onClick={this.onCreateBreakouts} onClick={this.onCreateBreakouts}
/> />

View File

@ -127,6 +127,10 @@ class BreakoutRoom extends Component {
preventClosing: true, preventClosing: true,
valid: true, valid: true,
}; };
this.breakoutFormId = _.uniqueId('breakout-form-');
this.freeJoinId = _.uniqueId('free-join-check-');
this.btnLevelId = _.uniqueId('btn-set-level-');
} }
componentDidMount() { componentDidMount() {
@ -295,7 +299,7 @@ class BreakoutRoom extends Component {
}; };
return ( return (
<div className={styles.boxContainer}> <div className={styles.boxContainer} key="rooms-grid-">
<label htmlFor="BreakoutRoom" className={!valid ? styles.changeToWarn : null}> <label htmlFor="BreakoutRoom" className={!valid ? styles.changeToWarn : null}>
<p <p
className={styles.freeJoinLabel} className={styles.freeJoinLabel}
@ -340,7 +344,7 @@ class BreakoutRoom extends Component {
if (isInvitation) return null; if (isInvitation) return null;
return ( return (
<div className={styles.breakoutSettings}> <div className={styles.breakoutSettings} key={this.breakoutFormId}>
<label htmlFor="numberOfRooms"> <label htmlFor="numberOfRooms">
<p className={styles.labelText}>{intl.formatMessage(intlMessages.numberOfRooms)}</p> <p className={styles.labelText}>{intl.formatMessage(intlMessages.numberOfRooms)}</p>
<select <select
@ -422,7 +426,7 @@ class BreakoutRoom extends Component {
if (isInvitation) return null; if (isInvitation) return null;
const { freeJoin } = this.state; const { freeJoin } = this.state;
return ( return (
<label htmlFor="freeJoinCheckbox" className={styles.freeJoinLabel}> <label htmlFor="freeJoinCheckbox" className={styles.freeJoinLabel} key={this.freeJoinId}>
<input <input
type="checkbox" type="checkbox"
className={styles.freeJoinCheckbox} className={styles.freeJoinCheckbox}
@ -534,6 +538,7 @@ class BreakoutRoom extends Component {
size="lg" size="lg"
label={label} label={label}
onClick={() => this.setState({ formFillLevel: level })} onClick={() => this.setState({ formFillLevel: level })}
key={this.btnLevelId}
/> />
); );
} }

View File

@ -10,6 +10,7 @@ import DropdownContent from '/imports/ui/components/dropdown/content/component';
import DropdownList from '/imports/ui/components/dropdown/list/component'; import DropdownList from '/imports/ui/components/dropdown/list/component';
import DropdownListItem from '/imports/ui/components/dropdown/list/item/component'; import DropdownListItem from '/imports/ui/components/dropdown/list/item/component';
import LockViewersContainer from '/imports/ui/components/lock-viewers/container'; import LockViewersContainer from '/imports/ui/components/lock-viewers/container';
import BreakoutRoom from '/imports/ui/components/actions-bar/create-breakout-room/component';
import { styles } from './styles'; import { styles } from './styles';
const propTypes = { const propTypes = {
@ -21,6 +22,11 @@ const propTypes = {
toggleMuteAllUsersExceptPresenter: PropTypes.func.isRequired, toggleMuteAllUsersExceptPresenter: PropTypes.func.isRequired,
toggleStatus: PropTypes.func.isRequired, toggleStatus: PropTypes.func.isRequired,
mountModal: PropTypes.func.isRequired, mountModal: PropTypes.func.isRequired,
users: PropTypes.arrayOf(Object).isRequired,
meetingName: PropTypes.string.isRequired,
createBreakoutRoom: PropTypes.func.isRequired,
meetingIsBreakout: PropTypes.bool.isRequired,
hasBreakoutRoom: PropTypes.bool.isRequired,
}; };
const intlMessages = defineMessages({ const intlMessages = defineMessages({
@ -68,6 +74,18 @@ const intlMessages = defineMessages({
id: 'app.userList.userOptions.muteAllExceptPresenterDesc', id: 'app.userList.userOptions.muteAllExceptPresenterDesc',
description: 'Mute all except presenter description', description: 'Mute all except presenter description',
}, },
createBreakoutRoom: {
id: 'app.actionsBar.actionsDropdown.createBreakoutRoom',
description: 'Create breakout room option',
},
createBreakoutRoomDesc: {
id: 'app.actionsBar.actionsDropdown.createBreakoutRoomDesc',
description: 'Description of create breakout room option',
},
invitationItem: {
id: 'app.invitation.title',
description: 'invitation to breakout title',
},
}); });
class UserOptions extends PureComponent { class UserOptions extends PureComponent {
@ -78,55 +96,19 @@ class UserOptions extends PureComponent {
isUserOptionsOpen: false, isUserOptionsOpen: false,
}; };
this.clearStatusId = _.uniqueId('list-item-');
this.muteId = _.uniqueId('list-item-');
this.muteAllId = _.uniqueId('list-item-');
this.lockId = _.uniqueId('list-item-');
this.createBreakoutId = _.uniqueId('list-item-');
this.onActionsShow = this.onActionsShow.bind(this); this.onActionsShow = this.onActionsShow.bind(this);
this.onActionsHide = this.onActionsHide.bind(this); this.onActionsHide = this.onActionsHide.bind(this);
this.alterMenu = this.alterMenu.bind(this); this.alterMenu = this.alterMenu.bind(this);
} this.handleCreateBreakoutRoomClick = this.handleCreateBreakoutRoomClick.bind(this);
this.onCreateBreakouts = this.onCreateBreakouts.bind(this);
componentWillMount() { this.onInvitationUsers = this.onInvitationUsers.bind(this);
const { this.renderMenuItems = this.renderMenuItems.bind(this);
intl,
isMeetingMuted,
mountModal,
toggleStatus,
toggleMuteAllUsers,
toggleMuteAllUsersExceptPresenter,
} = this.props;
this.menuItems = _.compact([
(<DropdownListItem
key={_.uniqueId('list-item-')}
icon="clear_status"
label={intl.formatMessage(intlMessages.clearAllLabel)}
description={intl.formatMessage(intlMessages.clearAllDesc)}
onClick={toggleStatus}
/>),
(<DropdownListItem
key={_.uniqueId('list-item-')}
icon="mute"
label={intl.formatMessage(intlMessages.muteAllLabel)}
description={intl.formatMessage(intlMessages.muteAllDesc)}
onClick={toggleMuteAllUsers}
/>),
(<DropdownListItem
key={_.uniqueId('list-item-')}
icon="mute"
label={intl.formatMessage(intlMessages.muteAllExceptPresenterLabel)}
description={intl.formatMessage(intlMessages.muteAllExceptPresenterDesc)}
onClick={toggleMuteAllUsersExceptPresenter}
/>),
(<DropdownListItem
key={_.uniqueId('list-item-')}
icon="lock"
label={intl.formatMessage(intlMessages.lockViewersLabel)}
description={intl.formatMessage(intlMessages.lockViewersDesc)}
onClick={() => mountModal(<LockViewersContainer />)}
/>),
]);
if (isMeetingMuted) {
this.alterMenu();
}
} }
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
@ -148,6 +130,40 @@ class UserOptions extends PureComponent {
}); });
} }
onCreateBreakouts() {
return this.handleCreateBreakoutRoomClick(false);
}
onInvitationUsers() {
return this.handleCreateBreakoutRoomClick(true);
}
handleCreateBreakoutRoomClick(isInvitation) {
const {
createBreakoutRoom,
mountModal,
meetingName,
users,
getUsersNotAssigned,
getBreakouts,
sendInvitation,
} = this.props;
return mountModal(
<BreakoutRoom
{...{
createBreakoutRoom,
meetingName,
users,
getUsersNotAssigned,
isInvitation,
getBreakouts,
sendInvitation,
}}
/>,
);
}
alterMenu() { alterMenu() {
const { const {
intl, intl,
@ -186,9 +202,92 @@ class UserOptions extends PureComponent {
} }
} }
renderMenuItems() {
const {
intl,
isMeetingMuted,
mountModal,
toggleStatus,
toggleMuteAllUsers,
toggleMuteAllUsersExceptPresenter,
meetingIsBreakout,
hasBreakoutRoom,
getUsersNotAssigned,
isUserModerator,
users,
} = this.props;
const canCreateBreakout = isUserModerator
&& !meetingIsBreakout
&& !hasBreakoutRoom;
const canInviteUsers = isUserModerator
&& !meetingIsBreakout
&& hasBreakoutRoom
&& getUsersNotAssigned(users).length;
this.menuItems = _.compact([
(<DropdownListItem
key={this.clearStatusId}
icon="clear_status"
label={intl.formatMessage(intlMessages.clearAllLabel)}
description={intl.formatMessage(intlMessages.clearAllDesc)}
onClick={toggleStatus}
/>),
(<DropdownListItem
key={this.muteAllId}
icon="mute"
label={intl.formatMessage(intlMessages.muteAllLabel)}
description={intl.formatMessage(intlMessages.muteAllDesc)}
onClick={toggleMuteAllUsers}
/>),
(<DropdownListItem
key={this.muteId}
icon="mute"
label={intl.formatMessage(intlMessages.muteAllExceptPresenterLabel)}
description={intl.formatMessage(intlMessages.muteAllExceptPresenterDesc)}
onClick={toggleMuteAllUsersExceptPresenter}
/>),
(<DropdownListItem
key={this.lockId}
icon="lock"
label={intl.formatMessage(intlMessages.lockViewersLabel)}
description={intl.formatMessage(intlMessages.lockViewersDesc)}
onClick={() => mountModal(<LockViewersContainer />)}
/>),
(canCreateBreakout
? (
<DropdownListItem
key={this.createBreakoutId}
icon="rooms"
label={intl.formatMessage(intlMessages.createBreakoutRoom)}
description={intl.formatMessage(intlMessages.createBreakoutRoomDesc)}
onClick={this.onCreateBreakouts}
/>
) : null
),
(canInviteUsers
? (
<DropdownListItem
icon="rooms"
label={intl.formatMessage(intlMessages.invitationItem)}
key={this.createBreakoutId}
onClick={this.onInvitationUsers}
/>
)
: null),
]);
if (isMeetingMuted) {
this.alterMenu();
}
return this.menuItems;
}
render() { render() {
const { intl } = this.props;
const { isUserOptionsOpen } = this.state; const { isUserOptionsOpen } = this.state;
const { intl } = this.props;
return ( return (
<Dropdown <Dropdown
@ -217,7 +316,7 @@ class UserOptions extends PureComponent {
> >
<DropdownList> <DropdownList>
{ {
this.menuItems this.renderMenuItems()
} }
</DropdownList> </DropdownList>
</DropdownContent> </DropdownContent>

View File

@ -1,67 +1,47 @@
import React, { PureComponent } from 'react'; import { withTracker } from 'meteor/react-meteor-data';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Auth from '/imports/ui/services/auth'; import Auth from '/imports/ui/services/auth';
import Service from '/imports/ui/components/actions-bar/service';
import UserOptions from './component'; import UserOptions from './component';
const propTypes = { const propTypes = {
users: PropTypes.arrayOf(Object).isRequired, users: PropTypes.arrayOf(Object).isRequired,
muteAllUsers: PropTypes.func.isRequired, muteAllUsers: PropTypes.func.isRequired,
muteAllExceptPresenter: PropTypes.func.isRequired, muteAllExceptPresenter: PropTypes.func.isRequired,
setEmojiStatus: PropTypes.func.isRequired, setEmojiStatus: PropTypes.func.isRequired,
meeting: PropTypes.shape({}).isRequired, meeting: PropTypes.shape({}).isRequired,
currentUser: PropTypes.shape({
isModerator: PropTypes.bool.isRequired,
}).isRequired,
}; };
export default class UserOptionsContainer extends PureComponent { const UserOptionsContainer = withTracker((props) => {
constructor(props) { const {
super(props); meeting,
users,
setEmojiStatus,
muteAllExceptPresenter,
muteAllUsers,
} = props;
const { meeting } = this.props; return {
toggleMuteAllUsers: () => muteAllUsers(Auth.userID),
this.state = { toggleMuteAllUsersExceptPresenter: () => muteAllExceptPresenter(Auth.userID),
meetingMuted: meeting.voiceProp.muteOnStart, toggleStatus: () => users.forEach(id => setEmojiStatus(id, 'none')),
isMeetingMuted: meeting.voiceProp.muteOnStart,
isUserPresenter: Service.isUserPresenter(),
isUserModerator: Service.isUserModerator(),
createBreakoutRoom: Service.createBreakoutRoom,
meetingIsBreakout: Service.meetingIsBreakout(),
hasBreakoutRoom: Service.hasBreakoutRoom(),
meetingName: Service.meetingName(),
users: Service.users(),
getBreakouts: Service.getBreakouts,
sendInvitation: Service.sendInvitation,
getUsersNotAssigned: Service.getUsersNotAssigned,
}; };
})(UserOptions);
this.muteMeeting = this.muteMeeting.bind(this);
this.muteAllUsersExceptPresenter = this.muteAllUsersExceptPresenter.bind(this);
this.handleClearStatus = this.handleClearStatus.bind(this);
}
muteMeeting() {
const { muteAllUsers } = this.props;
muteAllUsers(Auth.userID);
}
muteAllUsersExceptPresenter() {
const { muteAllExceptPresenter } = this.props;
muteAllExceptPresenter(Auth.userID);
}
handleClearStatus() {
const { users, setEmojiStatus } = this.props;
users.forEach((id) => {
setEmojiStatus(id, 'none');
});
}
render() {
const { currentUser } = this.props;
const currentUserIsModerator = currentUser.isModerator;
const { meetingMuted } = this.state;
return (
currentUserIsModerator
? (
<UserOptions
toggleMuteAllUsers={this.muteMeeting}
toggleMuteAllUsersExceptPresenter={this.muteAllUsersExceptPresenter}
toggleStatus={this.handleClearStatus}
isMeetingMuted={meetingMuted}
/>) : null
);
}
}
UserOptionsContainer.propTypes = propTypes; UserOptionsContainer.propTypes = propTypes;
export default UserOptionsContainer;

View File

@ -53,13 +53,13 @@ export function canGenerateIceCandidates() {
Session.set('canGenerateIceCandidates', true); Session.set('canGenerateIceCandidates', true);
resolve(); resolve();
} }
} };
pc.onicegatheringstatechange = function (e) { pc.onicegatheringstatechange = function (e) {
if (e.currentTarget.iceGatheringState == 'complete' && countIceCandidates == 0) reject(); if (e.currentTarget.iceGatheringState == 'complete' && countIceCandidates == 0) reject();
} };
setTimeout(function () { setTimeout(() => {
pc.close(); pc.close();
if (!countIceCandidates) reject(); if (!countIceCandidates) reject();
}, 5000); }, 5000);
@ -75,7 +75,7 @@ export function tryGenerateIceCandidates() {
canGenerateIceCandidates().then((ok) => { canGenerateIceCandidates().then((ok) => {
resolve(); resolve();
}).catch((e) => { }).catch((e) => {
navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then(function (stream) { navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then((stream) => {
canGenerateIceCandidates().then((ok) => { canGenerateIceCandidates().then((ok) => {
resolve(); resolve();
}).catch((e) => { }).catch((e) => {