Merge remote-tracking branch 'upstream/master' into Remove-finally

This commit is contained in:
Tainan Felipe 2018-11-22 15:02:23 -02:00
commit 77417c63f7
27 changed files with 593 additions and 46 deletions

View File

@ -133,10 +133,11 @@ muteButton.id = 'muteButton';
function getInitialState() {
document.getElementById('client-content').contentWindow.postMessage('c_recording_status', '*');
document.getElementById('client-content').contentWindow.postMessage('c_mute_status', '*');
document.getElementById('client-content').contentWindow.postMessage('get_audio_joined_status', '*');
}
function handleMessage(e) {
let neverJoinedAudio = true;
switch (e) {
case 'readyToConnect': {
// get initial state
@ -160,11 +161,18 @@ function handleMessage(e) {
case 'notInAudio': {
muteButton.innerHTML = 'Not in audio';
document.getElementById('muteButton').disabled = true;
if (neverJoinedAudio) {
// poll every 1 sec to check if we joined audio
setTimeout(function(){
document.getElementById('client-content').contentWindow.postMessage('get_audio_joined_status', '*');
}, 1000);}
break;
}
case 'joinedAudio': {
neverJoinedAudio = false;
muteButton.innerHTML = '';
document.getElementById('muteButton').disabled = false; getInitialState();
document.getElementById('muteButton').disabled = false;
document.getElementById('client-content').contentWindow.postMessage('c_mute_status', '*');
break;
}
default: console.log('neither', { e });

View File

@ -2,9 +2,13 @@ import { Meteor } from 'meteor/meteor';
import endMeeting from './methods/endMeeting';
import toggleRecording from './methods/toggleRecording';
import transferUser from './methods/transferUser';
import toggleLockSettings from './methods/toggleLockSettings';
import toggleWebcamsOnlyForModerator from './methods/toggleWebcamsOnlyForModerator';
Meteor.methods({
endMeeting,
toggleRecording,
toggleLockSettings,
transferUser,
toggleWebcamsOnlyForModerator,
});

View File

@ -0,0 +1,37 @@
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import RedisPubSub from '/imports/startup/server/redis';
export default function toggleLockSettings(credentials, meeting) {
const REDIS_CONFIG = Meteor.settings.private.redis;
const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
const EVENT_NAME = 'ChangeLockSettingsInMeetingCmdMsg';
const { meetingId, requesterUserId } = credentials;
check(meetingId, String);
check(requesterUserId, String);
check(meeting.lockSettingsProp, {
disableCam: Boolean,
disableMic: Boolean,
disablePrivChat: Boolean,
disablePubChat: Boolean,
lockedLayout: Boolean,
lockOnJoin: Boolean,
lockOnJoinConfigurable: Boolean,
setBy: String,
});
const payload = {
disableCam: meeting.lockSettingsProp.disableCam,
disableMic: meeting.lockSettingsProp.disableMic,
disablePrivChat: meeting.lockSettingsProp.disablePrivChat,
disablePubChat: meeting.lockSettingsProp.disablePubChat,
lockedLayout: meeting.lockSettingsProp.lockedLayout,
lockOnJoin: meeting.lockSettingsProp.lockOnJoin,
lockOnJoinConfigurable: meeting.lockSettingsProp.lockOnJoinConfigurable,
setBy: requesterUserId,
};
RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload);
}

View File

@ -0,0 +1,22 @@
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import RedisPubSub from '/imports/startup/server/redis';
export default function toggleWebcamsOnlyForModerator(credentials, meeting) {
const REDIS_CONFIG = Meteor.settings.private.redis;
const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
const EVENT_NAME = 'UpdateWebcamsOnlyForModeratorCmdMsg';
const { meetingId, requesterUserId } = credentials;
check(meetingId, String);
check(requesterUserId, String);
check(meeting.usersProp.webcamsOnlyForModerator, Boolean);
const payload = {
webcamsOnlyForModerator: meeting.usersProp.webcamsOnlyForModerator,
setBy: requesterUserId,
};
RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload);
}

View File

@ -69,10 +69,22 @@ export default function addMeeting(meeting) {
meetingId,
};
const lockSettingsProp = {
disableCam: false,
disableMic: false,
disablePrivChat: false,
disablePubChat: false,
lockOnJoin: true,
lockOnJoinConfigurable: false,
lockedLayout: false,
setBy: 'temp',
};
const modifier = {
$set: Object.assign(
{ meetingId },
flat(meeting, { safe: true }),
{ lockSettingsProp },
),
};

View File

@ -1,10 +1,13 @@
import Users from '/imports/api/users';
import PresentationPods from '/imports/api/presentation-pods';
import changeRole from '/imports/api/users/server/modifiers/changeRole';
import assignPresenter from '../methods/assignPresenter';
export default function handlePresenterAssigned({ body }, meetingId) {
export default function handlePresenterAssigned(credentials, meetingId) {
const USER_CONFIG = Meteor.settings.public.user;
const ROLE_PRESENTER = USER_CONFIG.role_presenter;
const { body } = credentials;
const { presenterId, assignedBy } = body;
changeRole(ROLE_PRESENTER, true, presenterId, meetingId, assignedBy);
@ -15,12 +18,35 @@ export default function handlePresenterAssigned({ body }, meetingId) {
presenter: true,
};
const defaultPodSelector = {
podId: 'DEFAULT_PRESENTATION_POD',
};
const prevPresenter = Users.findOne(selector);
// no previous presenters
// The below code is responsible for set Meeting presenter to be default pod presenter as well.
// It's been handled here because right now akka-apps don't handle all cases scenarios.
if (!prevPresenter) {
const currentDefaultPodPresenter = PresentationPods.findOne(defaultPodSelector);
const { currentPresenterId } = currentDefaultPodPresenter;
const podPresenterCredentials = {
meetingId,
requesterUserId: assignedBy,
};
if (currentDefaultPodPresenter.currentPresenterId !== '') {
const oldPresenter = Users.findOne({ userId: currentPresenterId });
if (oldPresenter.connectionStatus === 'offline') {
return assignPresenter(podPresenterCredentials, presenterId);
}
return true;
}
return assignPresenter(podPresenterCredentials, presenterId);
}
return changeRole(ROLE_PRESENTER, false, prevPresenter.userId, meetingId, assignedBy);
}

0
bigbluebutton-html5/imports/startup/server/logger.js Executable file → Normal file
View File

View File

@ -8,6 +8,7 @@ import Resizable from 're-resizable';
import browser from 'browser-detect';
import BreakoutRoomContainer from '/imports/ui/components/breakout-room/container';
import PollingContainer from '/imports/ui/components/polling/container';
import PollContainer from '/imports/ui/components/poll/container';
import ToastContainer from '../toast/container';
import ModalContainer from '../modal/container';
import NotificationsBarContainer from '../notifications-bar/container';
@ -16,7 +17,7 @@ import ChatAlertContainer from '../chat/alert/container';
import { styles } from './styles';
import UserListContainer from '../user-list/container';
import ChatContainer from '../chat/container';
import PollContainer from '/imports/ui/components/poll/container';
const MOBILE_MEDIA = 'only screen and (max-width: 40em)';
const USERLIST_COMPACT_WIDTH = 50;
@ -327,7 +328,7 @@ class App extends Component {
render() {
const {
userListIsOpen, customStyle, customStyleUrl,
userListIsOpen, customStyle, customStyleUrl, micsLocked,
} = this.props;
const { enableResize } = this.state;
@ -349,11 +350,11 @@ class App extends Component {
</section>
<PollingContainer />
<ModalContainer />
<AudioContainer />
{micsLocked ? null : <AudioContainer />}
<ToastContainer />
<ChatAlertContainer />
{ customStyleUrl ? <link rel="stylesheet" type="text/css" href={customStyleUrl} /> : null }
{ customStyle ? <link rel="stylesheet" type="text/css" href={`data:text/css;charset=UTF-8,${encodeURIComponent(customStyle)}`} /> : null }
{customStyleUrl ? <link rel="stylesheet" type="text/css" href={customStyleUrl} /> : null}
{customStyle ? <link rel="stylesheet" type="text/css" href={`data:text/css;charset=UTF-8,${encodeURIComponent(customStyle)}`} /> : null}
</main>
);
}

View File

@ -70,6 +70,7 @@ const AppContainer = (props) => {
export default injectIntl(withModalMounter(withTracker(({ intl, baseControls }) => {
const currentUser = Users.findOne({ userId: Auth.userID });
const meeting = Meetings.findOne({ meetingId: Auth.meetingID });
const isMeetingBreakout = meetingIsBreakout();
if (!currentUser.approved) {
@ -117,6 +118,7 @@ export default injectIntl(withModalMounter(withTracker(({ intl, baseControls })
pollIsOpen: Session.get('isPollOpen') && Session.get('isUserListOpen'),
customStyle: getFromUserSettings('customStyle', false),
customStyleUrl: getFromUserSettings('customStyleUrl', false),
micsLocked: (currentUser.locked && meeting.lockSettingsProp.disableMic),
};
})(AppContainer)));

View File

@ -3,6 +3,9 @@ import { withTracker } from 'meteor/react-meteor-data';
import { withModalMounter } from '/imports/ui/components/modal/service';
import AudioManager from '/imports/ui/services/audio-manager';
import { makeCall } from '/imports/ui/services/api';
import Users from '/imports/api/users/';
import Meetings from '/imports/api/meetings';
import Auth from '/imports/ui/services/auth';
import AudioControls from './component';
import AudioModalContainer from '../audio-modal/container';
import Service from '../service';
@ -15,11 +18,12 @@ const processToggleMuteFromOutside = (e) => {
makeCall('toggleSelfVoice');
break;
}
case 'c_mute_status': {
if (!AudioManager.isUsingAudio()) {
this.window.parent.postMessage({ response: 'notInAudio' }, '*');
return;
case 'get_audio_joined_status': {
const audioJoinedState = AudioManager.isConnected ? 'joinedAudio' : 'notInAudio';
this.window.parent.postMessage({ response: audioJoinedState }, '*');
break;
}
case 'c_mute_status': {
const muteState = AudioManager.isMuted ? 'selfMuted' : 'selfUnmuted';
this.window.parent.postMessage({ response: muteState }, '*');
break;
@ -39,6 +43,12 @@ export default withModalMounter(withTracker(({ mountModal }) =>
disable: Service.isConnecting() || Service.isHangingUp(),
glow: Service.isTalking() && !Service.isMuted(),
handleToggleMuteMicrophone: () => Service.toggleMuteMicrophone(),
handleJoinAudio: () => mountModal(<AudioModalContainer />),
handleJoinAudio: () => {
const meeting = Meetings.findOne({ meetingId: Auth.meetingID });
const currentUser = Users.findOne({ userId: Auth.userID });
const micsLocked = (currentUser.locked && meeting.lockSettingsProp.disableMic);
return micsLocked ? Service.joinListenOnly() : mountModal(<AudioModalContainer />);
},
handleLeaveAudio: () => Service.exitAudio(),
}))(AudioControlsContainer));

View File

@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import { defineMessages, injectIntl } from 'react-intl';
import { Meteor } from 'meteor/meteor';
import Button from '/imports/ui/components/button/component';
import logoutRouteHandler from '/imports/utils/logoutRouteHandler';
import { styles } from './styles';
const intlMessages = defineMessages({
@ -66,7 +67,7 @@ class ErrorScreen extends React.PureComponent {
<div className={styles.content}>
<Button
size="sm"
onClick={() => Session.set('isMeetingEnded', true)}
onClick={logoutRouteHandler}
label={intl.formatMessage(intlMessages.leave)}
/>
</div>

View File

@ -71,6 +71,7 @@ class JoinHandler extends Component {
return resp;
};
const setLogoutURL = (url) => Auth.logoutURL = url;
const setLogoURL = (resp) => {
setCustomLogoUrl(resp.customLogoURL);
return resp;
@ -94,6 +95,7 @@ class JoinHandler extends Component {
.then(response => response.json())
.then(({ response }) => response)
.then((resp) => {
setLogoutURL(resp.logoutURL);
if (resp.returncode !== 'FAILED') {
logger.info(`User successfully went through main.joinRouteHandler with [${resp}].`);
return resolve(resp);

View File

@ -0,0 +1,251 @@
import React, { Component } from 'react';
import { defineMessages, injectIntl } from 'react-intl';
import Button from '/imports/ui/components/button/component';
import Toggle from '/imports/ui/components/switch/component';
import cx from 'classnames';
import ModalBase from '/imports/ui/components/modal/base/component';
import { styles } from './styles';
const intlMessages = defineMessages({
lockViewersTitle: {
id: 'app.lock-viewers.title',
description: 'lock-viewers title',
},
closeLabel: {
id: 'app.shortcut-help.closeLabel',
description: 'label for close button',
},
closeDesc: {
id: 'app.shortcut-help.closeDesc',
description: 'description for close button',
},
lockViewersDescription: {
id: 'app.lock-viewers.description',
description: 'description for lock viewers feature',
},
featuresLable: {
id: 'app.lock-viewers.featuresLable',
description: 'features label',
},
lockStatusLabel: {
id: 'app.lock-viewers.lockStatusLabel',
description: 'description for close button',
},
webcamLabel: {
id: 'app.lock-viewers.webcamLabel',
description: 'description for close button',
},
otherViewersWebcamLabel: {
id: 'app.lock-viewers.otherViewersWebcamLabel',
description: 'description for close button',
},
microphoneLable: {
id: 'app.lock-viewers.microphoneLable',
description: 'description for close button',
},
publicChatLabel: {
id: 'app.lock-viewers.PublicChatLabel',
description: 'description for close button',
},
privateChatLable: {
id: 'app.lock-viewers.PrivateChatLable',
description: 'description for close button',
},
layoutLable: {
id: 'app.lock-viewers.Layout',
description: 'description for close button',
},
});
class LockViewersComponent extends Component {
constructor(props) {
super(props);
const {
closeModal,
toggleLockSettings,
toggleWebcamsOnlyForModerator,
} = props;
this.closeModal = closeModal;
this.toggleLockSettings = toggleLockSettings;
this.toggleWebcamsOnlyForModerator = toggleWebcamsOnlyForModerator;
}
render() {
const { intl, meeting } = this.props;
return (
<ModalBase
overlayClassName={styles.overlay}
className={styles.modal}
onRequestClose={this.closeModal}
>
<div className={styles.container}>
<div className={styles.header}>
<div className={styles.title}>{intl.formatMessage(intlMessages.lockViewersTitle)}</div>
<Button
data-test="modalBaseCloseButton"
className={styles.closeBtn}
label={intl.formatMessage(intlMessages.closeLabel)}
icon="close"
size="md"
hideLabel
onClick={this.closeModal}
/>
</div>
<div className={styles.description}>
{`${intl.formatMessage(intlMessages.lockViewersDescription)}`}
</div>
<div className={styles.form}>
<header className={styles.subHeader}>
<div className={styles.bold}>{intl.formatMessage(intlMessages.featuresLable)}</div>
<div className={styles.bold}>{intl.formatMessage(intlMessages.lockStatusLabel)}</div>
</header>
<div className={styles.row}>
<div className={styles.col} aria-hidden="true">
<div className={styles.formElement}>
<div className={styles.label}>
{intl.formatMessage(intlMessages.webcamLabel)}
</div>
</div>
</div>
<div className={styles.col}>
<div className={cx(styles.formElement, styles.pullContentRight)}>
<Toggle
icons={false}
defaultChecked={meeting.lockSettingsProp.disableCam}
onChange={() => {
meeting.lockSettingsProp.disableCam =
!meeting.lockSettingsProp.disableCam;
this.toggleLockSettings(meeting);
}}
ariaLabel={intl.formatMessage(intlMessages.webcamLabel)}
/>
</div>
</div>
</div>
<div className={styles.row}>
<div className={styles.col} aria-hidden="true">
<div className={styles.formElement}>
<div className={styles.label}>
{intl.formatMessage(intlMessages.otherViewersWebcamLabel)}
</div>
</div>
</div>
<div className={styles.col}>
<div className={cx(styles.formElement, styles.pullContentRight)}>
<Toggle
icons={false}
defaultChecked={meeting.usersProp.webcamsOnlyForModerator}
onChange={() => {
meeting.usersProp.webcamsOnlyForModerator =
!meeting.usersProp.webcamsOnlyForModerator;
this.toggleWebcamsOnlyForModerator(meeting);
}}
ariaLabel={intl.formatMessage(intlMessages.otherViewersWebcamLabel)}
/>
</div>
</div>
</div>
<div className={styles.row}>
<div className={styles.col} aria-hidden="true">
<div className={styles.formElement}>
<div className={styles.label}>
{intl.formatMessage(intlMessages.microphoneLable)}
</div>
</div>
</div>
<div className={styles.col}>
<div className={cx(styles.formElement, styles.pullContentRight)}>
<Toggle
icons={false}
defaultChecked={meeting.lockSettingsProp.disableMic}
onChange={() => {
meeting.lockSettingsProp.disableMic =
!meeting.lockSettingsProp.disableMic;
this.toggleLockSettings(meeting);
}}
ariaLabel={intl.formatMessage(intlMessages.microphoneLable)}
/>
</div>
</div>
</div>
<div className={styles.row}>
<div className={styles.col} aria-hidden="true">
<div className={styles.formElement}>
<div className={styles.label}>
{intl.formatMessage(intlMessages.publicChatLabel)}
</div>
</div>
</div>
<div className={styles.col}>
<div className={cx(styles.formElement, styles.pullContentRight)}>
<Toggle
icons={false}
defaultChecked={meeting.lockSettingsProp.disablePubChat}
onChange={() => {
meeting.lockSettingsProp.disablePubChat =
!meeting.lockSettingsProp.disablePubChat;
this.toggleLockSettings(meeting);
}}
ariaLabel={intl.formatMessage(intlMessages.publicChatLabel)}
/>
</div>
</div>
</div>
<div className={styles.row}>
<div className={styles.col} aria-hidden="true">
<div className={styles.formElement}>
<div className={styles.label}>
{intl.formatMessage(intlMessages.privateChatLable)}
</div>
</div>
</div>
<div className={styles.col}>
<div className={cx(styles.formElement, styles.pullContentRight)}>
<Toggle
icons={false}
defaultChecked={meeting.lockSettingsProp.disablePrivChat}
onChange={() => {
meeting.lockSettingsProp.disablePrivChat =
!meeting.lockSettingsProp.disablePrivChat;
this.toggleLockSettings(meeting);
}}
ariaLabel={intl.formatMessage(intlMessages.privateChatLable)}
/>
</div>
</div>
</div>
<div className={styles.row}>
<div className={styles.col} aria-hidden="true">
<div className={styles.formElement}>
<div className={styles.label}>
{intl.formatMessage(intlMessages.layoutLable)}
</div>
</div>
</div>
<div className={styles.col}>
<div className={cx(styles.formElement, styles.pullContentRight)}>
<Toggle
icons={false}
defaultChecked={meeting.lockSettingsProp.lockedLayout}
onChange={() => {
meeting.lockSettingsProp.lockedLayout =
!meeting.lockSettingsProp.lockedLayout;
this.toggleLockSettings(meeting);
}}
ariaLabel={intl.formatMessage(intlMessages.layoutLable)}
/>
</div>
</div>
</div>
</div>
</div>
</ModalBase>
);
}
}
export default injectIntl(LockViewersComponent);

View File

@ -0,0 +1,24 @@
import React from 'react';
import { withTracker } from 'meteor/react-meteor-data';
import { withModalMounter } from '/imports/ui/components/modal/service';
import { makeCall } from '/imports/ui/services/api';
import Meetings from '/imports/api/meetings';
import Auth from '/imports/ui/services/auth';
import LockViewersComponent from './component';
const LockViewersContainer = props => <LockViewersComponent {...props} />;
export default withModalMounter(withTracker(({ mountModal }) => ({
closeModal() {
mountModal(null);
},
toggleLockSettings(meeting) {
makeCall('toggleLockSettings', meeting);
},
toggleWebcamsOnlyForModerator(meeting) {
makeCall('toggleWebcamsOnlyForModerator', meeting);
},
meeting: (Meetings.findOne({ meetingId: Auth.meetingID })),
}))(LockViewersContainer));

View File

@ -0,0 +1,118 @@
@import '/imports/ui/stylesheets/mixins/focus';
@import '/imports/ui/stylesheets/variables/_all';
@import "/imports/ui/components/modal/simple/styles";
:root {
--modal-margin: 3rem;
--title-position-left: 2.2rem;
--closeBtn-position-left: 2.5rem;
}
.title {
position: relative;
left: var(--title-position-left);
color: var(--color-gray-dark);
font-weight: bold;
font-size: var(--font-size-large);
text-align: center;
}
.form {
display: flex;
flex-flow: column;
}
.container {
margin: var(--modal-margin);
margin-bottom: var(--lg-padding-x);
}
.subHeader {
display: flex;
flex-flow: row;
flex-grow: 1;
justify-content: space-between;
color: var(--color-gray-label);
font-size: var(--font-size-base);
margin-bottom: var(--title-position-left);
}
.modal {
@extend .modal;
padding: var(--jumbo-padding-y);
}
.overlay {
@extend .overlay;
}
.description {
text-align: center;
color: var(--color-gray);
margin-bottom: var(--jumbo-padding-y)
}
.row {
display: flex;
flex-flow: row;
flex-grow: 1;
justify-content: space-between;
margin-bottom: var(--md-padding-x);
}
.col {
display: flex;
flex-grow: 1;
flex-basis: 0;
margin-right: var(--md-padding-x);
}
.label {
color: var(--color-gray-label);
font-size: var(--font-size-small);
margin-bottom: var(--lg-padding-y);
}
.formElement {
position: relative;
display: flex;
flex-flow: column;
flex-grow: 1;
}
.pullContentRight {
display: flex;
justify-content: flex-end;
flex-flow: row;
}
.bold {
font-weight: bold;
}
.closeBtn {
position: relative;
background-color: var(--color-white);
left: var(--closeBtn-position-left);
bottom: var(--closeBtn-position-left);
i {
color: var(--color-gray-light);
}
&:focus,
&:hover {
background-color: var(--color-gray-lighter);
i {
color: var(--color-gray);
}
}
}
.header {
margin: 0;
padding: 0;
border: none;
line-height: var(--title-position-left);
margin-bottom: var(--lg-padding-y);
}

View File

@ -5,6 +5,7 @@ import { Meteor } from 'meteor/meteor';
import Auth from '/imports/ui/services/auth';
import Button from '/imports/ui/components/button/component';
import getFromUserSettings from '/imports/ui/services/users-settings';
import logoutRouteHandler from '/imports/utils/logoutRouteHandler';
import Rating from './rating/component';
import { styles } from './styles';
@ -77,19 +78,6 @@ class MeetingEnded extends React.PureComponent {
const comment = textarea.value;
return comment;
}
static logoutRouteHandler() {
Auth.logout()
.then((logoutURL = window.location.origin) => {
const protocolPattern = /^((http|https):\/\/)/;
window.location.href =
protocolPattern.test(logoutURL) ?
logoutURL :
`http://${logoutURL}`;
});
}
constructor(props) {
super(props);
this.state = {
@ -116,7 +104,7 @@ class MeetingEnded extends React.PureComponent {
} = this.state;
if (selected <= 0) {
MeetingEnded.logoutRouteHandler();
logoutRouteHandler();
return;
}
@ -138,7 +126,7 @@ class MeetingEnded extends React.PureComponent {
fetch(url, options)
.finally(() => {
MeetingEnded.logoutRouteHandler();
logoutRouteHandler();
});
}

View File

@ -121,6 +121,11 @@
color: var(--color-gray-light);
}
.userListColumn {
@extend %flex-column;
min-height: 0;
}
.enter,
.appear {
opacity: 0.01;

View File

@ -12,7 +12,8 @@
.container{
display: flex;
align-items: center;
margin-bottom: .3rem;
margin-bottom: var(--lg-padding-y);
margin-top: var(--sm-padding-x);
}
.scrollableList {

View File

@ -175,7 +175,7 @@ class UserParticipants extends Component {
} = this.props;
return (
<div>
<div className={styles.userListColumn}>
{
!compact ?
<div className={styles.container}>

View File

@ -9,6 +9,7 @@ import DropdownTrigger from '/imports/ui/components/dropdown/trigger/component';
import DropdownContent from '/imports/ui/components/dropdown/content/component';
import DropdownList from '/imports/ui/components/dropdown/list/component';
import DropdownListItem from '/imports/ui/components/dropdown/list/item/component';
import LockViewersContainer from '/imports/ui/components/lock-viewers/container';
import { styles } from './styles';
const propTypes = {
@ -19,7 +20,6 @@ const propTypes = {
toggleMuteAllUsers: PropTypes.func.isRequired,
toggleMuteAllUsersExceptPresenter: PropTypes.func.isRequired,
toggleStatus: PropTypes.func.isRequired,
toggleLockView: PropTypes.func.isRequired,
};
const intlMessages = defineMessages({
@ -83,7 +83,8 @@ class UserOptions extends Component {
}
componentWillMount() {
const { intl, isMeetingMuted } = this.props;
const { intl, isMeetingMuted, mountModal } = this.props;
this.menuItems = _.compact([
(<DropdownListItem
key={_.uniqueId('list-item-')}
@ -111,7 +112,7 @@ class UserOptions extends Component {
icon="lock"
label={intl.formatMessage(intlMessages.lockViewersLabel)}
description={intl.formatMessage(intlMessages.lockViewersDesc)}
onClick={this.props.toggleLockView}
onClick={() => mountModal(<LockViewersContainer />)}
/>),
]);

View File

@ -1,6 +1,5 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import logger from '/imports/startup/client/logger';
import Auth from '/imports/ui/services/auth';
import mapUser from '/imports/ui/services/user/mapUser';
import Users from '/imports/api/users/';
@ -26,7 +25,6 @@ export default class UserOptionsContainer extends Component {
this.muteMeeting = this.muteMeeting.bind(this);
this.muteAllUsersExceptPresenter = this.muteAllUsersExceptPresenter.bind(this);
this.handleLockView = this.handleLockView.bind(this);
this.handleClearStatus = this.handleClearStatus.bind(this);
}
@ -44,11 +42,6 @@ export default class UserOptionsContainer extends Component {
muteAllExceptPresenter(currentUser.userId);
}
handleLockView() {
// Temporary lock method, will be changed in future PR
logger.info('handleLockView function');
}
handleClearStatus() {
const { users, setEmojiStatus } = this.props;
@ -71,7 +64,6 @@ export default class UserOptionsContainer extends Component {
<UserOptions
toggleMuteAllUsers={this.muteMeeting}
toggleMuteAllUsersExceptPresenter={this.muteAllUsersExceptPresenter}
toggleLockView={this.handleLockView}
toggleStatus={this.handleClearStatus}
isMeetingMuted={this.state.meetingMuted}
/> : null

View File

@ -388,7 +388,7 @@ class VideoProvider extends Component {
}
async createWebRTCPeer(id, shareWebcam) {
const { meetingId, sessionToken } = this.props;
const { meetingId, sessionToken, voiceBridge } = this.props;
let iceServers = [];
try {
@ -448,6 +448,7 @@ class VideoProvider extends Component {
sdpOffer: offerSdp,
cameraId: id,
meetingId,
voiceBridge,
};
this.sendMessage(message);

View File

@ -14,4 +14,5 @@ export default withTracker(() => ({
sessionToken: VideoService.sessionToken(),
userName: VideoService.userName(),
enableVideoStats: getFromUserSettings('enableVideoStats', Meteor.settings.public.kurento.enableVideoStats),
voiceBridge: VideoService.voiceBridge(),
}))(VideoProviderContainer);

View File

@ -126,6 +126,11 @@ class VideoService {
return Auth.sessionToken;
}
voiceBridge() {
const voiceBridge = Meetings.findOne({ meetingId: Auth.meetingID }).voiceProp.voiceConf;
return voiceBridge;
}
isConnected() {
return this.isConnected;
}
@ -157,4 +162,5 @@ export default {
meetingId: () => videoService.meetingId(),
getAllUsersVideo: () => videoService.getAllUsersVideo(),
sessionToken: () => videoService.sessionToken(),
voiceBridge: () => videoService.voiceBridge(),
};

View File

@ -0,0 +1,15 @@
import Auth from '/imports/ui/services/auth';
const logoutRouteHandler = () => {
Auth.logout()
.then((logoutURL = window.location.origin) => {
const protocolPattern = /^((http|https):\/\/)/;
window.location.href =
protocolPattern.test(logoutURL) ?
logoutURL :
`http://${logoutURL}`;
});
};
export default logoutRouteHandler;

View File

@ -359,6 +359,16 @@
"app.shortcut-help.closePrivateChat": "Close Private Chat",
"app.shortcut-help.openActions": "Open Actions Menu",
"app.shortcut-help.openStatus": "Open Status Menu",
"app.lock-viewers.title": "Lock Viewers",
"app.lock-viewers.description": "These options enable you to restrict certain features available to viewers, such as locking out their ability to use private chat. (These restrictions do no apply to moderators)",
"app.lock-viewers.featuresLable": "Feature",
"app.lock-viewers.lockStatusLabel": "Locked Status",
"app.lock-viewers.webcamLabel": "Webcam",
"app.lock-viewers.otherViewersWebcamLabel": "See other viewsers webcams",
"app.lock-viewers.microphoneLable": "Microphone",
"app.lock-viewers.PublicChatLabel": "Public Chat",
"app.lock-viewers.PrivateChatLable": "Private Chat",
"app.lock-viewers.Layout": "Layout",
"app.videoPreview.cameraLabel": "Camera",
"app.videoPreview.cancelLabel": "Cancel",
"app.videoPreview.closeLabel": "Close",

View File

@ -8,8 +8,17 @@ if [[ $files = *"bigbluebutton-html5"* ]]; then
meteor npm install
if [ $1 = linter ]
then
html5_files=""
list=$(echo $files | tr " " "\n")
for file in $list
do
if [[ $file = bigbluebutton-html5* ]] ; then
html5_files+=" $file"
fi
done
cd ..
bigbluebutton-html5/node_modules/.bin/eslint --ext .jsx,.js $files
bigbluebutton-html5/node_modules/.bin/eslint --ext .jsx,.js $html5_files
elif [ $1 = acceptance_tests ]
then
{