Merge pull request #5827 from Tainan404/issue-5562

Allow html5 user to join in any breakout room
This commit is contained in:
Anton Georgiev 2018-07-12 12:21:14 -04:00 committed by GitHub
commit 97fb0e9b4f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 154 additions and 41 deletions

View File

@ -6,5 +6,6 @@ import handleBreakoutClosed from './handlers/breakoutClosed';
RedisPubSub.on('BreakoutRoomStartedEvtMsg', handleBreakoutStarted);
RedisPubSub.on('BreakoutRoomJoinURLEvtMsg', handleBreakoutJoinURL);
RedisPubSub.on('RequestBreakoutJoinURLRespMsg', handleBreakoutJoinURL);
RedisPubSub.on('BreakoutRoomsTimeRemainingUpdateEvtMsg', handleUpdateTimeRemaining);
RedisPubSub.on('BreakoutRoomEndedEvtMsg', handleBreakoutClosed);

View File

@ -1,3 +1,6 @@
import { Meteor } from 'meteor/meteor';
import requestJoinURL from './methods/requestJoinURL';
Meteor.methods({});
Meteor.methods({
requestJoinURL,
});

View File

@ -0,0 +1,25 @@
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import RedisPubSub from '/imports/startup/server/redis';
export default function requestJoinURL(credentials, { breakoutId }) {
const REDIS_CONFIG = Meteor.settings.private.redis;
const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
const { meetingId, requesterUserId, requesterToken } = credentials;
check(meetingId, String);
check(requesterUserId, String);
check(requesterToken, String);
const eventName = 'RequestBreakoutJoinURLReqMsg';
return RedisPubSub.publishUserMessage(
CHANNEL, eventName, meetingId, requesterUserId,
{
meetingId,
breakoutId,
userId: requesterUserId,
},
);
}

View File

@ -12,12 +12,18 @@ function breakouts(credentials) {
Logger.info(`Publishing Breakouts for ${meetingId} ${requesterUserId}`);
const selector = {
$or: [{
parentMeetingId: meetingId,
'users.userId': requesterUserId,
}, {
breakoutId: meetingId,
},
$or: [
{
parentMeetingId: meetingId,
freeJoin: true,
},
{
parentMeetingId: meetingId,
'users.userId': requesterUserId,
},
{
breakoutId: meetingId,
},
],
};

View File

@ -3,6 +3,7 @@ import { defineMessages, injectIntl } from 'react-intl';
import { withModalMounter } from '/imports/ui/components/modal/service';
import Modal from '/imports/ui/components/modal/fullscreen/component';
import AudioService from '../audio/service';
import { styles } from './styles';
const intlMessages = defineMessages({
title: {
@ -13,6 +14,10 @@ const intlMessages = defineMessages({
id: 'app.breakoutJoinConfirmation.message',
description: 'Join breakout confim message',
},
freeJoinMessage: {
id: 'app.breakoutJoinConfirmation.freeJoinMessage',
description: 'Join breakout confim message',
},
confirmLabel: {
id: 'app.breakoutJoinConfirmation.confirmLabel',
description: 'Join confirmation button label',
@ -35,24 +40,54 @@ class BreakoutJoinConfirmation extends Component {
constructor(props) {
super(props);
this.state = {
selectValue: props.breakout.breakoutId,
};
this.handleJoinBreakoutConfirmation = this.handleJoinBreakoutConfirmation.bind(this);
this.renderSelectMeeting = this.renderSelectMeeting.bind(this);
this.handleSelectChange = this.handleSelectChange.bind(this);
}
handleJoinBreakoutConfirmation() {
const {
breakoutURL,
getURL,
mountModal,
breakoutURL,
isFreeJoin,
} = this.props;
const url = isFreeJoin ? getURL(this.state.selectValue) : breakoutURL;
// leave main room's audio when joining a breakout room
AudioService.exitAudio();
window.open(breakoutURL);
window.open(url);
mountModal(null);
}
handleSelectChange(e) {
const { value } = e.target;
this.setState({ selectValue: value });
this.props.requestJoinURL(value);
}
renderSelectMeeting() {
const { breakouts, intl } = this.props;
return (
<div className={styles.selectParent}>
{`${intl.formatMessage(intlMessages.freeJoinMessage)}`}
<select
className={styles.select}
value={this.state.selectValue}
onChange={this.handleSelectChange}
>
{breakouts.map(({ name, breakoutId }) => (<option key={breakoutId} value={breakoutId} >{name}</option>))}
</select>
</div>
);
}
render() {
const { intl, breakoutName } = this.props;
const { intl, breakoutName, isFreeJoin } = this.props;
return (
<Modal
title={intl.formatMessage(intlMessages.title)}
@ -66,7 +101,7 @@ class BreakoutJoinConfirmation extends Component {
description: intl.formatMessage(intlMessages.dismissDesc),
}}
>
{`${intl.formatMessage(intlMessages.message)} ${breakoutName}?`}
{ isFreeJoin ? this.renderSelectMeeting() : `${intl.formatMessage(intlMessages.message)} ${breakoutName}?`}
</Modal>
);
}

View File

@ -0,0 +1,43 @@
import React from 'react';
import { withTracker } from 'meteor/react-meteor-data';
import Breakouts from '/imports/api/breakouts';
import Auth from '/imports/ui/services/auth';
import { makeCall } from '/imports/ui/services/api';
import navBarService from '/imports/ui/components/nav-bar/service';
import BreakoutJoinConfirmationComponent from './component';
const BreakoutJoinConfirmationContrainer = props =>
(<BreakoutJoinConfirmationComponent {...props} />);
const getURL = (breakoutId) => {
const currentUserId = Auth.userID;
const getBreakout = Breakouts.findOne({ breakoutId });
const user = getBreakout ? getBreakout.users.find(u => u.userId === currentUserId) : '';
if (user) return user.redirectToHtml5JoinURL;
return '';
};
const requestJoinURL = (breakoutId) => {
makeCall('requestJoinURL', {
breakoutId,
});
};
export default withTracker(({ breakout, mountModal, breakoutName }) => {
const isFreeJoin = breakout.freeJoin;
const { breakoutId } = breakout;
const url = getURL(breakoutId);
if (isFreeJoin && !url) {
requestJoinURL(breakoutId);
}
return {
isFreeJoin,
mountModal,
breakoutName,
breakoutURL: url,
breakouts: navBarService.getBreakouts(),
requestJoinURL,
getURL,
};
})(BreakoutJoinConfirmationContrainer);

View File

@ -0,0 +1,14 @@
@import "../../stylesheets/variables/_all";
.selectParent {
display: flex;
flex-direction: column;
align-items: center;
}
.select {
background-color: $color-white;
width: 50%;
margin: 1rem;
border-color: $color-gray-lighter;
}

View File

@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import _ from 'lodash';
import cx from 'classnames';
import Icon from '/imports/ui/components/icon/component';
import BreakoutJoinConfirmation from '/imports/ui/components/breakout-join-confirmation/component';
import BreakoutJoinConfirmation from '/imports/ui/components/breakout-join-confirmation/container';
import Dropdown from '/imports/ui/components/dropdown/component';
import DropdownTrigger from '/imports/ui/components/dropdown/trigger/component';
import DropdownContent from '/imports/ui/components/dropdown/content/component';
@ -58,9 +58,9 @@ const defaultProps = {
const SHORTCUTS_CONFIG = Meteor.settings.public.app.shortcuts;
const TOGGLE_USERLIST_AK = SHORTCUTS_CONFIG.toggleUserList.accesskey;
const openBreakoutJoinConfirmation = (breakoutURL, breakoutName, mountModal) =>
const openBreakoutJoinConfirmation = (breakout, breakoutName, mountModal) =>
mountModal(<BreakoutJoinConfirmation
breakoutURL={breakoutURL}
breakout={breakout}
breakoutName={breakoutName}
/>);
@ -83,6 +83,9 @@ class NavBar extends Component {
this.props.toggleUserList();
}
shouldComponentUpdate(nextProps) {
return nextProps.breakouts.length !== this.props.breakouts.length;
}
componentDidUpdate(oldProps) {
const {
breakouts,
@ -102,10 +105,8 @@ class NavBar extends Component {
return;
}
const breakoutURL = getBreakoutJoinURL(breakout);
if (!this.state.didSendBreakoutInvite && !isBreakoutRoom) {
this.inviteUserToBreakout(breakout, breakoutURL);
this.inviteUserToBreakout(breakout);
}
});
@ -114,13 +115,13 @@ class NavBar extends Component {
}
}
inviteUserToBreakout(breakout, breakoutURL) {
inviteUserToBreakout(breakout) {
const {
mountModal,
} = this.props;
this.setState({ didSendBreakoutInvite: true }, () => {
openBreakoutJoinConfirmation.call(this, breakoutURL, breakout.name, mountModal);
openBreakoutJoinConfirmation.call(this, breakout, breakout.name, mountModal);
});
}
@ -136,6 +137,7 @@ class NavBar extends Component {
<h1 className={styles.presentationTitle}>{presentationTitle}</h1>
);
}
const breakoutItems = breakouts.map(breakout => this.renderBreakoutItem(breakout));
return (
<Dropdown isOpen={this.state.isActionsOpen}>
@ -148,7 +150,7 @@ class NavBar extends Component {
placement="bottom"
>
<DropdownList>
{breakouts.map(breakout => this.renderBreakoutItem(breakout))}
{breakoutItems}
</DropdownList>
</DropdownContent>
</Dropdown>
@ -157,19 +159,17 @@ class NavBar extends Component {
renderBreakoutItem(breakout) {
const {
getBreakoutJoinURL,
mountModal,
} = this.props;
const breakoutName = breakout.name;
const breakoutURL = getBreakoutJoinURL(breakout);
return (
<DropdownListItem
className={styles.actionsHeader}
key={_.uniqueId('action-header')}
label={breakoutName}
onClick={openBreakoutJoinConfirmation.bind(this, breakoutURL, breakoutName, mountModal)}
onClick={openBreakoutJoinConfirmation.bind(this, breakout, breakoutName, mountModal)}
/>
);
}

View File

@ -59,7 +59,6 @@ export default withRouter(withTracker(({ location, router }) => {
breakouts,
currentUserId,
meetingId,
getBreakoutJoinURL: Service.getBreakoutJoinURL,
presentationTitle: meetingTitle,
hasUnreadMessages: checkUnreadMessages(),
isBreakoutRoom: meetingIsBreakout(),

View File

@ -1,22 +1,8 @@
import Auth from '/imports/ui/services/auth';
import Breakouts from '/imports/api/breakouts';
const getBreakouts = () => Breakouts.find().fetch();
const getBreakoutJoinURL = (breakout) => {
const currentUserId = Auth.userID;
if (breakout.users) {
const user = breakout.users.find(u => u.userId === currentUserId);
if (user) {
return user.redirectToHtml5JoinURL;
}
}
return '';
};
const getBreakouts = () => Breakouts.find({}, { sort: { sequence: 1 } }).fetch();
export default {
getBreakouts,
getBreakoutJoinURL,
};

View File

@ -242,6 +242,7 @@
"app.breakoutJoinConfirmation.confirmDesc": "Join you to the Breakout Room",
"app.breakoutJoinConfirmation.dismissLabel": "Cancel",
"app.breakoutJoinConfirmation.dismissDesc": "Closes and rejects Joining the Breakout Room",
"app.breakoutJoinConfirmation.freeJoinMessage": "Choose a Breakout Room to join",
"app.breakoutTimeRemainingMessage": "Breakout Room time remaining: {0}",
"app.breakoutWillCloseMessage": "Time ended. Breakout Room will close soon",
"app.calculatingBreakoutTimeRemaining": "Calculating remaining time...",