diff --git a/bigbluebutton-html5/imports/api/breakouts/server/eventHandlers.js b/bigbluebutton-html5/imports/api/breakouts/server/eventHandlers.js
old mode 100644
new mode 100755
index 40df1e7d3d..48d54c7de9
--- a/bigbluebutton-html5/imports/api/breakouts/server/eventHandlers.js
+++ b/bigbluebutton-html5/imports/api/breakouts/server/eventHandlers.js
@@ -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);
diff --git a/bigbluebutton-html5/imports/api/breakouts/server/methods.js b/bigbluebutton-html5/imports/api/breakouts/server/methods.js
old mode 100644
new mode 100755
index 6ce8893022..9958a42d5e
--- a/bigbluebutton-html5/imports/api/breakouts/server/methods.js
+++ b/bigbluebutton-html5/imports/api/breakouts/server/methods.js
@@ -1,3 +1,6 @@
import { Meteor } from 'meteor/meteor';
+import requestJoinURL from './methods/requestJoinURL';
-Meteor.methods({});
+Meteor.methods({
+ requestJoinURL,
+});
diff --git a/bigbluebutton-html5/imports/api/breakouts/server/methods/requestJoinURL.js b/bigbluebutton-html5/imports/api/breakouts/server/methods/requestJoinURL.js
new file mode 100755
index 0000000000..a2a5c19789
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/breakouts/server/methods/requestJoinURL.js
@@ -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,
+ },
+ );
+}
diff --git a/bigbluebutton-html5/imports/api/breakouts/server/publishers.js b/bigbluebutton-html5/imports/api/breakouts/server/publishers.js
old mode 100644
new mode 100755
index e0828ded3f..98f321c29a
--- a/bigbluebutton-html5/imports/api/breakouts/server/publishers.js
+++ b/bigbluebutton-html5/imports/api/breakouts/server/publishers.js
@@ -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,
+ },
],
};
diff --git a/bigbluebutton-html5/imports/ui/components/breakout-join-confirmation/component.jsx b/bigbluebutton-html5/imports/ui/components/breakout-join-confirmation/component.jsx
old mode 100644
new mode 100755
index f281b03f8f..d5ed0e13a6
--- a/bigbluebutton-html5/imports/ui/components/breakout-join-confirmation/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/breakout-join-confirmation/component.jsx
@@ -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 (
+
+ {`${intl.formatMessage(intlMessages.freeJoinMessage)}`}
+
+
+ );
+ }
+
render() {
- const { intl, breakoutName } = this.props;
+ const { intl, breakoutName, isFreeJoin } = this.props;
return (
- {`${intl.formatMessage(intlMessages.message)} ${breakoutName}?`}
+ { isFreeJoin ? this.renderSelectMeeting() : `${intl.formatMessage(intlMessages.message)} ${breakoutName}?`}
);
}
diff --git a/bigbluebutton-html5/imports/ui/components/breakout-join-confirmation/container.jsx b/bigbluebutton-html5/imports/ui/components/breakout-join-confirmation/container.jsx
new file mode 100755
index 0000000000..91a23bed43
--- /dev/null
+++ b/bigbluebutton-html5/imports/ui/components/breakout-join-confirmation/container.jsx
@@ -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 =>
+ ();
+
+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);
diff --git a/bigbluebutton-html5/imports/ui/components/breakout-join-confirmation/styles.scss b/bigbluebutton-html5/imports/ui/components/breakout-join-confirmation/styles.scss
new file mode 100755
index 0000000000..5225e42035
--- /dev/null
+++ b/bigbluebutton-html5/imports/ui/components/breakout-join-confirmation/styles.scss
@@ -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;
+}
\ No newline at end of file
diff --git a/bigbluebutton-html5/imports/ui/components/nav-bar/component.jsx b/bigbluebutton-html5/imports/ui/components/nav-bar/component.jsx
old mode 100644
new mode 100755
index 4842a69476..5d5c28b135
--- a/bigbluebutton-html5/imports/ui/components/nav-bar/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/nav-bar/component.jsx
@@ -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();
@@ -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 {
{presentationTitle}
);
}
+ const breakoutItems = breakouts.map(breakout => this.renderBreakoutItem(breakout));
return (
@@ -148,7 +150,7 @@ class NavBar extends Component {
placement="bottom"
>
- {breakouts.map(breakout => this.renderBreakoutItem(breakout))}
+ {breakoutItems}
@@ -157,19 +159,17 @@ class NavBar extends Component {
renderBreakoutItem(breakout) {
const {
- getBreakoutJoinURL,
mountModal,
} = this.props;
const breakoutName = breakout.name;
- const breakoutURL = getBreakoutJoinURL(breakout);
return (
);
}
diff --git a/bigbluebutton-html5/imports/ui/components/nav-bar/container.jsx b/bigbluebutton-html5/imports/ui/components/nav-bar/container.jsx
old mode 100644
new mode 100755
index ec1f569e17..a2c025fca6
--- a/bigbluebutton-html5/imports/ui/components/nav-bar/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/nav-bar/container.jsx
@@ -59,7 +59,6 @@ export default withRouter(withTracker(({ location, router }) => {
breakouts,
currentUserId,
meetingId,
- getBreakoutJoinURL: Service.getBreakoutJoinURL,
presentationTitle: meetingTitle,
hasUnreadMessages: checkUnreadMessages(),
isBreakoutRoom: meetingIsBreakout(),
diff --git a/bigbluebutton-html5/imports/ui/components/nav-bar/service.js b/bigbluebutton-html5/imports/ui/components/nav-bar/service.js
old mode 100644
new mode 100755
index 816ff4f643..56522370cb
--- a/bigbluebutton-html5/imports/ui/components/nav-bar/service.js
+++ b/bigbluebutton-html5/imports/ui/components/nav-bar/service.js
@@ -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,
};
+
diff --git a/bigbluebutton-html5/private/locales/en.json b/bigbluebutton-html5/private/locales/en.json
index fea8077a65..2b74264f1a 100755
--- a/bigbluebutton-html5/private/locales/en.json
+++ b/bigbluebutton-html5/private/locales/en.json
@@ -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...",