Merge pull request #3834 from antobinary/audio-restructuring

[HTML5] Audio restructuring part 1
This commit is contained in:
Anton Georgiev 2017-04-20 16:00:59 -04:00 committed by GitHub
commit a9f2a18475
44 changed files with 367 additions and 502 deletions

View File

@ -0,0 +1,14 @@
export default class BaseAudioBridge {
constructor() {
}
exitAudio () {
}
joinListenOnly() {
}
joinMicrophone() {
}
}

View File

@ -1,32 +1,30 @@
// TODO: This file should be a `service.js` somewhere in the /ui folder
import Users from '/imports/api/users';
import Meetings from '/imports/api/meetings';
import Auth from '/imports/ui/services/auth';
import {callServer} from '/imports/ui/services/api';
import {vertoExitAudio, vertoJoinListenOnly, vertoJoinMicrophone} from '/imports/api/verto';
import BaseAudioBridge from './base';
import { callServer } from '/imports/ui/services/api';
const APP_CONFIG = Meteor.settings.public.app;
const MEDIA_CONFIG = Meteor.settings.public.media;
let triedHangup = false;
function getVoiceBridge() {
return Meetings.findOne({}).voiceConf;
}
export default class SIPBridge extends BaseAudioBridge {
constructor(userData) {
super();
this.userData = userData;
}
function amIListenOnly() {
const uid = Auth.userID;
return Users.findOne({ userId: uid }).user.listenOnly;
}
joinListenOnly() {
callServer('listenOnlyToggle', true);
this._joinVoiceCallSIP({ isListenOnly: true });
}
// Periodically check the status of the WebRTC call, when a call has been established attempt to
// hangup, retry if a call is in progress, send the leave voice conference message to BBB
joinMicrophone() {
this._joinVoiceCallSIP({ isListenOnly: false });
}
function exitAudio(afterExitCall = () => {}) {
if (!MEDIA_CONFIG.useSIPAudio) {
vertoExitAudio();
return;
} else {
// Periodically check the status of the WebRTC call, when a call has been established attempt to
// hangup, retry if a call is in progress, send the leave voice conference message to BBB
exitAudio(afterExitCall = () => {}) {
// To be called when the hangup is initiated
const hangupCallback = function () {
console.log('Exiting Voice Conference');
@ -46,7 +44,7 @@ function exitAudio(afterExitCall = () => {}) {
console.log('Attempting to hangup on WebRTC call');
// notify BBB-apps we are leaving the call call if we are listen only
if (amIListenOnly()) {
if (userData.listenOnly) {
callServer('listenOnlyToggle', false);
}
@ -71,27 +69,29 @@ function exitAudio(afterExitCall = () => {}) {
(this, afterExitCall);
return false;
};
}
}
// join the conference. If listen only send the request to the server
function joinVoiceCallSIP(options) {
const extension = getVoiceBridge();
console.log(options);
if (MEDIA_CONFIG.useSIPAudio) {
// join the conference. If listen only send the request to the server
_joinVoiceCallSIP(options) {
const extension = this.userData.voiceBridge;
console.log(options);
// create voice call params
const joinCallback = function (message) {
console.log('Beginning WebRTC Conference Call');
};
const {
userId,
username,
} = this.userData;
window.BBB = {};
window.BBB.getMyUserInfo = function (callback) {
const uid = Auth.userID;
const result = {
myUserID: uid,
myUsername: Users.findOne({ userId: uid }).user.name,
myInternalUserID: uid,
myUserID: userId,
myUsername: username,
myInternalUserID: userId,
myAvatarURL: null,
myRole: 'getMyRole',
amIPresenter: 'false',
@ -101,51 +101,30 @@ function joinVoiceCallSIP(options) {
return callback(result);
};
const m = Meetings.findOne();
const st = {
stun: m.stuns,
turn: m.turns,
const stunsAndTurns = {
stun: this.userData.stuns,
turn: this.userData.turns,
};
callIntoConference(extension, function (audio) {
switch (audio.status) {
case 'failed':
let audioFailed = new CustomEvent('bbb.webrtc.failed', {
status: 'Failed' });
status: 'Failed', });
window.dispatchEvent(audioFailed);
break;
case 'mediafail':
let mediaFailed = new CustomEvent('bbb.webrtc.mediaFailed', {
status: 'MediaFailed' });
status: 'MediaFailed', });
window.dispatchEvent(mediaFailed);
break;
case 'mediasuccess':
case 'started':
let connected = new CustomEvent('bbb.webrtc.connected', {
status: 'started' });
status: 'started', });
window.dispatchEvent(connected);
break;
}
}, options.isListenOnly, st);
return;
}, options.isListenOnly, stunsAndTurns);
}
}
function joinListenOnly() {
callServer('listenOnlyToggle', true);
if (MEDIA_CONFIG.useSIPAudio) {
joinVoiceCallSIP({ isListenOnly: true });
} else {
vertoJoinListenOnly();
}
}
function joinMicrophone() {
if (MEDIA_CONFIG.useSIPAudio) {
joinVoiceCallSIP({ isListenOnly: false });
} else {
vertoJoinMicrophone();
}
}
export { joinListenOnly, joinMicrophone, exitAudio, getVoiceBridge, };

View File

@ -0,0 +1,38 @@
import { getVoiceBridge } from '/imports/ui/components/audio/service';
import BaseAudioBridge from './base';
export default class VertoBridge extends BaseAudioBridge {
constructor(userData) {
super();
const {
username,
voiceBridge,
} = userData;
this.voiceBridge = voiceBridge;
this.vertoUsername = 'FreeSWITCH User - ' + encodeURIComponent(username);
}
exitAudio() {
window.vertoExitAudio();
}
joinListenOnly() {
window.vertoJoinListenOnly(
'remote-media',
this.voiceBridge,
this.vertoUsername,
null,
);
}
joinMicrophone() {
window.vertoJoinMicrophone(
'remote-media',
this.voiceBridge,
this.vertoUsername,
null,
);
}
}

View File

@ -0,0 +1,31 @@
import { callServer } from '/imports/ui/services/api';
import BaseAudioBridge from '../bridge/base';
import VertoBridge from '../bridge/verto';
import SIPBridge from '../bridge/sip';
// manages audio calls and audio bridges
export default class AudioManager {
constructor(userData) {
const MEDIA_CONFIG = Meteor.settings.public.media;
const audioBridge = MEDIA_CONFIG.useSIPAudio ? new SIPBridge(userData) : new VertoBridge(userData);
if (!(audioBridge instanceof BaseAudioBridge)) {
throw 'Audio Bridge not compatible';
}
this.bridge = audioBridge;
}
exitAudio () {
this.bridge.exitAudio();
}
joinAudio(listenOnly) {
if (listenOnly) {
callServer('listenOnlyToggle', true);
this.bridge.joinListenOnly();
} else {
this.bridge.joinMicrophone();
}
}
}

View File

@ -0,0 +1,5 @@
import VertoBridge from './verto';
const deskshareBridge = new VertoBridge();
export default deskshareBridge;

View File

@ -0,0 +1,28 @@
import Users from '/imports/api/users';
import Auth from '/imports/ui/services/auth';
import { getVoiceBridge } from '/imports/ui/components/audio/service';
// TODO pass info in constructor instead of importing ^^
const createVertoUserName = () => {
const userId = Auth.userID;
const uName = Users.findOne({ userId }).user.name;
return 'FreeSWITCH User - ' + encodeURIComponent(uName);
};
export default class VertoDeskshareBridge {
constructor() {
}
vertoWatchVideo() {
window.vertoWatchVideo(
'deskshareVideo',
getVoiceBridge(),
createVertoUserName(),
null,
null,
null,
);
}
// TODO add vertoExitVideo
}

View File

@ -1,52 +0,0 @@
import {getInStorage} from '/imports/ui/components/app/service';
import Users from '/imports/api/users';
import Auth from '/imports/ui/services/auth';
import { getVoiceBridge } from '/imports/api/phone';
function createVertoUserName() {
const uid = Auth.userID;
const uName = Users.findOne({ userId: uid }).user.name;
const conferenceUsername = encodeURIComponent(uName);
return conferenceUsername;
}
function vertoExitAudio() {
window.vertoExitAudio();
}
function vertoJoinListenOnly() {
window.vertoJoinListenOnly(
'remote-media',
getVoiceBridge(),
createVertoUserName(),
null,
);
}
function vertoJoinMicrophone() {
window.vertoJoinMicrophone(
'remote-media',
getVoiceBridge(),
createVertoUserName(),
null,
);
}
function vertoWatchVideo() {
window.vertoWatchVideo(
'deskshareVideo',
getVoiceBridge(),
createVertoUserName(),
null,
null,
null,
);
}
export {
vertoJoinListenOnly,
vertoJoinMicrophone,
vertoWatchVideo,
vertoExitAudio,
};

View File

@ -1,11 +1,9 @@
import React, { Component, PropTypes } from 'react';
import Button from '/imports/ui/components/button/component';
import React, { Component } from 'react';
import styles from './styles.scss';
import EmojiContainer from './emoji-menu/container';
import ActionsDropdown from './actions-dropdown/component';
import JoinAudioOptionsContainer from './audio-menu/container';
import JoinAudioOptionsContainer from '../audio/audio-menu/container';
import MuteAudioContainer from './mute-button/container';
import JoinVideo from './video-button/component';
export default class ActionsBar extends Component {
constructor(props) {

View File

@ -2,6 +2,7 @@ import React, { Component } from 'react';
import { createContainer } from 'meteor/react-meteor-data';
import ActionsBar from './component';
import Service from './service';
import { exitAudio, handleJoinAudio } from '../audio/service';
class ActionsBarContainer extends Component {
constructor(props) {
@ -20,8 +21,8 @@ class ActionsBarContainer extends Component {
export default createContainer(() => {
const isPresenter = Service.isUserPresenter();
const handleExitAudio = () => Service.handleExitAudio();
const handleOpenJoinAudio = () => Service.handleJoinAudio();
const handleExitAudio = () => exitAudio();
const handleOpenJoinAudio = () => handleJoinAudio();
return {
isUserPresenter: isPresenter,

View File

@ -1,10 +1,6 @@
import React from 'react';
import AuthSingleton from '/imports/ui/services/auth/index.js';
import Users from '/imports/api/users';
import { joinListenOnly } from '/imports/api/phone';
import { showModal } from '/imports/ui/components/app/service';
import { exitAudio } from '/imports/api/phone';
import Audio from '/imports/ui/components/audio-modal/component';
let isUserPresenter = () => {
@ -14,17 +10,6 @@ let isUserPresenter = () => {
}).user.presenter;
};
const handleExitAudio = () => {
return exitAudio();
}
const handleJoinAudio = () => {
const handleJoinListenOnly = () => joinListenOnly();
return showModal(<Audio handleJoinListenOnly={handleJoinListenOnly} />);
}
export default {
isUserPresenter,
handleJoinAudio,
handleExitAudio,
};

View File

@ -1,11 +1,11 @@
import React, { Component, PropTypes } from 'react';
import _ from 'lodash';
import { defineMessages, injectIntl } from 'react-intl';
import NotificationsBarContainer from '../notifications-bar/container';
import AudioNotificationContainer from '../audio-notification/container';
import AudioNotificationContainer from '../audio/audio-notification/container';
import AudioContainer from '../audio/container';
import ChatNotificationContainer from '../chat/notification/container';
import Button from '../button/component';
import styles from './styles';
import cx from 'classnames';
@ -16,15 +16,15 @@ const intlMessages = defineMessages({
},
chatLabel: {
id: 'app.chat.Label',
description: 'Aria-label for Chat Section'
description: 'Aria-label for Chat Section',
},
mediaLabel: {
id: 'app.media.Label',
description: 'Aria-label for Media Section'
description: 'Aria-label for Media Section',
},
actionsbarLabel: {
id: 'app.actionsBar.Label',
description: 'Aria-label for ActionsBar Section'
description: 'Aria-label for ActionsBar Section',
},
});
@ -165,7 +165,7 @@ class App extends Component {
{this.renderSidebar()}
</section>
{modal}
<audio id="remote-media" autoPlay="autoplay"></audio>
<AudioContainer />
<ChatNotificationContainer currentChatID={params.chatID} />
</main>
);

View File

@ -5,7 +5,6 @@ import { defineMessages, injectIntl } from 'react-intl';
import {
getModal,
showModal,
getFontSize,
getCaptionsStatus,
} from './service';
@ -18,7 +17,6 @@ import App from './component';
import NavBarContainer from '../nav-bar/container';
import ActionsBarContainer from '../actions-bar/container';
import MediaContainer from '../media/container';
import AudioModalContainer from '../audio-modal/container';
import ClosedCaptionsContainer from '/imports/ui/components/closed-captions/container';
const defaultProps = {
@ -48,12 +46,8 @@ class AppContainer extends Component {
}
};
const APP_CONFIG = Meteor.settings.public.app;
const init = () => {
if (APP_CONFIG.autoJoinAudio) {
showModal(<AudioModalContainer />);
}
// TODO
};
export default withRouter(injectIntl(createContainer(({ router, intl, baseControls }) => {

View File

@ -1,91 +0,0 @@
import React from 'react';
import { defineMessages, injectIntl } from 'react-intl';
import Button from '/imports/ui/components/button/component';
import { clearModal } from '/imports/ui/components/app/service';
import styles from '../styles.scss';
import DeviceSelector from '/imports/ui/components/audio/device-selector/component';
import AudioTestContainer from '/imports/ui/components/audio-test/container';
import EnterAudioContainer from '/imports/ui/components/enter-audio/container';
const intlMessages = defineMessages({
backLabel: {
id: 'app.audio.listenOnly.backLabel',
},
closeLabel: {
id: 'app.audio.listenOnly.closeLabel',
},
});
class ListenOnly extends React.Component {
constructor(props) {
super(props);
this.chooseAudio = this.chooseAudio.bind(this);
this.handleOutputChange = this.handleOutputChange.bind(this);
this.handleClose = this.handleClose.bind(this);
this.state = {
inputDeviceId: undefined,
};
}
chooseAudio() {
this.props.changeMenu(this.props.JOIN_AUDIO);
}
handleOutputChange(deviceId) {
console.log(`OUTPUT DEVICE CHANGED: ${deviceId}`);
}
handleClose() {
this.setState({ isOpen: false });
clearModal();
}
render() {
const {
intl
} = this.props;
return (
<div>
<div className={styles.center}>
<Button className={styles.backBtn}
label={intl.formatMessage(intlMessages.backLabel)}
icon={'left_arrow'}
size={'md'}
color={'primary'}
ghost={true}
onClick={this.chooseAudio}
/>
<Button className={styles.closeBtn}
label={intl.formatMessage(intlMessages.closeLabel)}
icon={'close'}
size={'lg'}
circle={true}
hideLabel={true}
onClick={this.handleClose}
/>
<div>
Choose your listen only settings
</div>
</div>
<div>
<div className={styles.containerLeftHalfContent}>
<DeviceSelector
className={styles.item}
kind="audiooutput"
onChange={this.handleOutputChange} />
<AudioTestContainer />
</div>
<div className={styles.containerRightHalfContent}>
<EnterAudioContainer isFullAudio={false}/>
</div>
</div>
</div>
);
}
};
export default injectIntl(ListenOnly);

View File

@ -7,7 +7,7 @@ import { defineMessages, injectIntl } from 'react-intl';
const intlMessages = defineMessages({
joinAudio: {
id: 'app.audio.joinAudio',
description: 'Join audio button label'
description: 'Join audio button label',
},
leaveAudio: {
id: 'app.audio.leaveAudio',

View File

@ -1,6 +1,5 @@
import React from 'react';
import { createContainer } from 'meteor/react-meteor-data';
import Button from '/imports/ui/components/button/component';
import Users from '/imports/api/users/index';
import Auth from '/imports/ui/services/auth/index';
import JoinAudioOptions from './component';

View File

@ -1,14 +1,8 @@
import React from 'react';
import Icon from '/imports/ui/components/icon/component';
import Button from '/imports/ui/components/button/component';
import ModalBase from '../modal/base/component';
import { clearModal } from '/imports/ui/components/app/service';
import classNames from 'classnames';
import ReactDOM from 'react-dom';
import ModalBase from '../../modal/base/component';
import styles from './styles.scss';
import JoinAudio from './join-audio/component';
import ListenOnly from './listen-only/component';
import AudioSettings from './audio-settings/component';
import JoinAudio from '../join-audio/component';
import AudioSettings from '../audio-settings/component';
export default class Audio extends React.Component {
constructor(props) {
@ -16,7 +10,6 @@ export default class Audio extends React.Component {
this.JOIN_AUDIO = 0;
this.AUDIO_SETTINGS = 1;
this.LISTEN_ONLY = 2;
this.submenus = [];
}
@ -28,7 +21,6 @@ export default class Audio extends React.Component {
this.setState({ activeSubmenu: 0 });
this.submenus.push({ componentName: JoinAudio, });
this.submenus.push({ componentName: AudioSettings, });
this.submenus.push({ componentName: ListenOnly, });
}
changeMenu(i) {

View File

@ -1,7 +1,7 @@
import React, { Component, PropTypes } from 'react';
import React, { Component } from 'react';
import { createContainer } from 'meteor/react-meteor-data';
import Audio from './component';
import { joinListenOnly } from '/imports/api/phone';
import { joinListenOnly } from '../service';
export default class AudioModalContainer extends Component {
constructor(props) {

View File

@ -1,4 +1,4 @@
@import "../../stylesheets/variables/_all";
@import "../../../stylesheets/variables/_all";
.center {
display: flex;
@ -41,7 +41,7 @@ Button.audioBtn:hover span:first-child {
Button.audioBtn span:last-child {
color: black;
font-size: 0.8rem;
font-weight: 500;
font-weight: 600;
}
Button.audioBtn:first-of-type {

View File

@ -42,7 +42,7 @@ class AudioNotification extends Component {
intl,
} = this.props;
if (!color || !message ){
if (!color || !message) {
return null;
} else {
return (

View File

@ -1,10 +1,7 @@
import { Meteor } from 'meteor/meteor';
import { createContainer } from 'meteor/react-meteor-data';
import React, { Component, PropTypes } from 'react';
import React, { Component } from 'react';
import { defineMessages, injectIntl } from 'react-intl';
import AudioNotification from './component';
import styles from './styles.scss';
import Button from '/imports/ui/components/button/component';
const intlMessages = defineMessages({
audioFailed: {
@ -26,7 +23,7 @@ class AudioNotificationContainer extends Component {
this.state = {
status: null,
}
};
this.handleAudioFailure = this.handleAudioFailure.bind(this);
this.handleMediaFailure = this.handleMediaFailure.bind(this);
@ -34,42 +31,42 @@ class AudioNotificationContainer extends Component {
}
componentDidMount() {
window.addEventListener("bbb.webrtc.failed", this.handleAudioFailure);
window.addEventListener("bbb.webrtc.mediaFailed", this.handleMediaFailure);
window.addEventListener("bbb.webrtc.connected", this.handleClose);
window.addEventListener('bbb.webrtc.failed', this.handleAudioFailure);
window.addEventListener('bbb.webrtc.mediaFailed', this.handleMediaFailure);
window.addEventListener('bbb.webrtc.connected', this.handleClose);
}
componentWillUnmount() {
window.removeEventListener("bbb.webrtc.failed", this.handleAudioFailure);
window.removeEventListener("bbb.webrtc.mediaFailed", this.handleMediaFailure);
window.removeEventListener("bbb.webrtc.connected", this.handleClose);
window.removeEventListener('bbb.webrtc.failed', this.handleAudioFailure);
window.removeEventListener('bbb.webrtc.mediaFailed', this.handleMediaFailure);
window.removeEventListener('bbb.webrtc.connected', this.handleClose);
}
handleClose() {
this.color = null;
this.message = null;
this.setState({status: null});
this.setState({ status: null });
}
handleAudioFailure() {
this.message = this.props.audioFailure;
this.setState({status: 'failed'});
this.setState({ status: 'failed' });
}
handleMediaFailure() {
this.message = this.props.mediaFailure;
this.setState({status: 'failed'});
this.setState({ status: 'failed' });
}
render() {
const handleClose = this.handleClose;
this.color = 'danger';
return(
return (
<AudioNotification
color={this.color}
message={this.message}
handleClose={this.handleClose}
handleClose={handleClose}
/>
);
}

View File

@ -1,4 +1,4 @@
@import "../../stylesheets/variables/_all";
@import "../../../stylesheets/variables/_all";
$nb-default-color: $color-gray;
$nb-default-bg: $color-white;

View File

@ -2,12 +2,12 @@ import React from 'react';
import { defineMessages, injectIntl } from 'react-intl';
import Button from '/imports/ui/components/button/component';
import { clearModal } from '/imports/ui/components/app/service';
import styles from '../styles.scss';
import styles from '../audio-modal/styles.scss';
import DeviceSelector from '/imports/ui/components/audio/device-selector/component';
import AudioStreamVolume from '/imports/ui/components/audio/audio-stream-volume/component';
import EnterAudioContainer from '/imports/ui/components/enter-audio/container';
import AudioTestContainer from '/imports/ui/components/audio-test/container';
import EnterAudioContainer from '/imports/ui/components/audio/enter-audio/container';
import AudioTestContainer from '/imports/ui/components/audio/audio-test/container';
import cx from 'classnames';
class AudioSettings extends React.Component {
@ -112,7 +112,7 @@ class AudioSettings extends React.Component {
</div>
</div>
<div className={styles.col}>
<label className={styles.label}>&nbsp;</label>
<label className={styles.label}> </label>
<AudioTestContainer/>
</div>
</div>
@ -150,7 +150,7 @@ const intlMessages = defineMessages({
streamVolumeLabel: {
id: 'app.audio.audioSettings.microphoneStreamLabel',
description: 'Label for stream volume',
}
},
});
export default injectIntl(AudioSettings);

View File

@ -9,8 +9,8 @@ const propTypes = {
const defaultProps = {
low: 0,
optimum: .05,
high: .3,
optimum: 0.05,
high: 0.3,
deviceId: undefined,
};

View File

@ -1,35 +1,35 @@
import React from 'react';
import Button from '/imports/ui/components/button/component';
import styles from '../settings/styles.scss';
import styles from './styles.scss';
import { defineMessages, injectIntl } from 'react-intl';
class AudioTest extends React.Component {
constructor(props) {
super(props);
}
render() {
const {
intl,
} = this.props;
return (
<Button className={styles.testAudioBtn}
label={intl.formatMessage(intlMessages.playSoundLabel)}
icon={'unmute'}
size={'md'}
color={'primary'}
onClick={this.props.handlePlayAudioSample}
/>
);
}
};
const intlMessages = defineMessages({
playSoundLabel: {
id: 'app.audio.playSoundLabel',
description: 'Play sound button label',
},
});
export default injectIntl(AudioTest);
super(props);
}
render() {
const {
intl,
} = this.props;
return (
<Button className={styles.testAudioBtn}
label={intl.formatMessage(intlMessages.playSoundLabel)}
icon={'unmute'}
size={'sm'}
color={'primary'}
onClick={this.props.handlePlayAudioSample}
/>
);
}
};
const intlMessages = defineMessages({
playSoundLabel: {
id: 'app.audio.playSoundLabel',
description: 'Play sound button label',
},
});
export default injectIntl(AudioTest);

View File

@ -1,27 +1,26 @@
import React, { Component, PropTypes } from 'react';
import { createContainer } from 'meteor/react-meteor-data';
import AudioTest from './component';
class AudioTestContainer extends Component {
constructor(props) {
super(props);
}
render() {
return (
<AudioTest {...this.props}>
{this.props.children}
</AudioTest>
);
}
}
export default createContainer(() => {
const data = {
handlePlayAudioSample: () => {
const snd = new Audio('resources/sounds/audioSample.mp3');
snd.play();
},
};
return data;
}, AudioTestContainer);
import React, { Component } from 'react';
import { createContainer } from 'meteor/react-meteor-data';
import AudioTest from './component';
class AudioTestContainer extends Component {
constructor(props) {
super(props);
}
render() {
return (
<AudioTest {...this.props}>
{this.props.children}
</AudioTest>
);
}
}
export default createContainer(() => {
return {
handlePlayAudioSample: () => {
const snd = new Audio('resources/sounds/audioSample.mp3');
snd.play();
},
};
}, AudioTestContainer);

View File

@ -0,0 +1,12 @@
@import "../../../stylesheets/variables/all";
.testAudioBtn {
border: none;
padding-left: 0;
background-color: transparent;
color: $color-primary;
font-weight: normal;
i {
color: $color-primary;
}
}

View File

@ -0,0 +1,27 @@
import React, { Component } from 'react';
import { init } from './service';
import { showModal } from '../app/service';
import AudioModalContainer from './audio-modal/container';
export default class Audio extends Component {
constructor(props) {
super(props);
}
componentWillMount() {
if (this.props.showJoinAudio) {
showModal(<AudioModalContainer />);
}
}
componentDidMount() {
init();
}
render() {
return (
<audio id="remote-media" autoPlay="autoplay">
</audio>
);
}
}

View File

@ -1,11 +1,12 @@
import React, { Component, PropTypes } from 'react';
import React, { Component } from 'react';
import { createContainer } from 'meteor/react-meteor-data';
import Audio from './component';
class AudioContainer extends Component {
render() {
return (
<Audio {...this.props}>
<Audio
{...this.props}>
{this.props.children}
</Audio>
);
@ -13,5 +14,9 @@ class AudioContainer extends Component {
}
export default createContainer(() => {
return {};
const APP_CONFIG = Meteor.settings.public.app;
return {
showJoinAudio: APP_CONFIG.autoJoinAudio,
};
}, AudioContainer);

View File

@ -1,7 +1,6 @@
import React from 'react';
import { defineMessages, injectIntl } from 'react-intl';
import Button from '/imports/ui/components/button/component';
import styles from '../settings/styles.scss';
class EnterAudio extends React.Component {
constructor(props) {
@ -14,8 +13,8 @@ class EnterAudio extends React.Component {
} = this.props;
return (
<div className={styles.half}>
<Button className={styles.enterBtn}
<div>
<Button
label={intl.formatMessage(intlMessages.enterSessionLabel)}
size={'md'}
color={'primary'}

View File

@ -1,6 +1,6 @@
import React, { Component, PropTypes } from 'react';
import React, { Component } from 'react';
import { createContainer } from 'meteor/react-meteor-data';
import {joinListenOnly, joinMicrophone} from '/imports/api/phone';
import { joinListenOnly, joinMicrophone } from '../service';
import { clearModal } from '/imports/ui/components/app/service';
import EnterAudio from './component';

View File

@ -1,5 +1,5 @@
import React from 'react';
import styles from '../styles.scss';
import styles from '../audio-modal/styles.scss';
import Button from '/imports/ui/components/button/component';
import { clearModal } from '/imports/ui/components/app/service';
import { defineMessages, injectIntl } from 'react-intl';

View File

@ -0,0 +1,49 @@
import React from 'react';
import AudioModal from './audio-modal/component';
import Meetings from '/imports/api/meetings';
import Users from '/imports/api/users';
import Auth from '/imports/ui/services/auth';
import { showModal } from '/imports/ui/components/app/service';
import AudioManager from '/imports/api/audio/client/manager';
const handleJoinAudio = () => {
const handleJoinListenOnly = () => joinListenOnly();
return showModal(<AudioModal handleJoinListenOnly={handleJoinListenOnly} />);
};
let audioManager = undefined;
const init = () => {
const userId = Auth.userID;
const User = Users.findOne({ userId });
const username = User.user.name;
const listenOnly = User.user.listenOnly;
const Meeting = Meetings.findOne(); //TODO test this with Breakouts
const turns = Meeting.turns;
const stuns = Meeting.stuns;
const voiceBridge = Meeting.voiceBridge;
const userData = {
userId,
username,
listenOnly,
turns,
stuns,
voiceBridge,
};
audioManager = new AudioManager(userData);
};
let exitAudio = () => audioManager.exitAudio();
let joinListenOnly = () => audioManager.joinAudio(true);
let joinMicrophone = () => audioManager.joinAudio(false);
export {
init,
handleJoinAudio,
exitAudio,
joinListenOnly,
joinMicrophone,
};

View File

@ -1,7 +1,7 @@
import React, { Component } from 'react';
import { defineMessages, injectIntl } from 'react-intl';
import { clearModal } from '/imports/ui/components/app/service';
import { exitAudio } from '/imports/api/phone'
import { exitAudio } from '../audio/service'
import Modal from '/imports/ui/components/modal/component';
const intlMessages = defineMessages({

View File

@ -1,7 +1,9 @@
import Deskshare from '/imports/api/deskshare';
import {vertoWatchVideo} from '/imports/api/verto';
import VertoBridge from '/imports/api/deskshare/client/bridge/verto';
import Auth from '/imports/ui/services/auth';
const vertoBridge = new VertoBridge();
// when the meeting information has been updated check to see if it was
// desksharing. If it has changed either trigger a call to receive video
// and display it, or end the call and hide the video
@ -18,14 +20,14 @@ function isVideoBroadcasting() {
function presenterDeskshareHasEnded() {
// references a functiion in the global namespace inside verto_extension.js
// that we load dynamically
vertoExitVideo();
vertoBridge.vertoExitVideo();
};
// if remote deskshare has been started connect and display the video stream
function presenterDeskshareHasStarted() {
// references a functiion in the global namespace inside verto_extension.js
// that we load dynamically
vertoWatchVideo();
vertoBridge.vertoWatchVideo();
};
export {

View File

@ -53,6 +53,7 @@ export default class ModalBase extends Component {
overlayClassName={styleOverlay}
portalClassName={styles.portal}
isOpen={isOpen}
contentLabel="modal"
onAfterOpen={this.handleAfterOpen}
onRequestClose={this.handleRequestClose}>
{this.props.children}

View File

@ -3,7 +3,6 @@ import Modal from '/imports/ui/components/modal/component';
import { Tab, Tabs, TabList, TabPanel } from 'react-tabs';
import { defineMessages, injectIntl } from 'react-intl';
import ClosedCaptions from '/imports/ui/components/settings/submenus/closed-captions/component';
import Audio from '/imports/ui/components/settings/submenus/audio/component';
import Application from '/imports/ui/components/settings/submenus/application/container';
import Participants from '/imports/ui/components/settings/submenus/participants/component';
import Video from '/imports/ui/components/settings/submenus/video/component';
@ -42,7 +41,6 @@ class Settings extends Component {
constructor(props) {
super(props);
const audio = props.audio;
const video = props.video;
const application = props.application;
const cc = props.cc;
@ -50,14 +48,12 @@ class Settings extends Component {
this.state = {
current: {
audio: _.clone(audio),
video: _.clone(video),
application: _.clone(application),
cc: _.clone(cc),
participants: _.clone(participants),
},
saved: {
audio: _.clone(audio),
video: _.clone(video),
application: _.clone(application),
cc: _.clone(cc),
@ -135,10 +131,6 @@ class Settings extends Component {
<Icon iconName='application' className={styles.icon}/>
<span id="appTab">{intl.formatMessage(intlMessages.appTabLabel)}</span>
</Tab>
<Tab className={styles.tabSelector} aria-labelledby="audioTab">
<Icon iconName='unmute' className={styles.icon}/>
<span id="audioTab">{intl.formatMessage(intlMessages.audioTabLabel)}</span>
</Tab>
<Tab className={styles.tabSelector} aria-labelledby="videoTab">
<Icon iconName='video' className={styles.icon}/>
<span id="videoTab">{intl.formatMessage(intlMessages.videoTabLabel)}</span>
@ -161,11 +153,6 @@ class Settings extends Component {
settings={this.state.current.application}
/>
</TabPanel>
<TabPanel className={styles.tabPanel}>
<Audio
settings={this.state.current.audio}
handleUpdateSettings={this.handleUpdateSettings}/>
</TabPanel>
<TabPanel className={styles.tabPanel}>
<Video
handleUpdateSettings={this.handleUpdateSettings}

View File

@ -1,6 +1,5 @@
import React, { Component, PropTypes } from 'react';
import { createContainer } from 'meteor/react-meteor-data';
import _ from 'lodash';
import Settings from './component';
import SettingsService from '/imports/ui/services/settings';

View File

@ -56,13 +56,3 @@
padding-top: 0;
padding-right: 0;
}
.testAudioBtn {
border: none;
padding-left: 0;
i {
color: $color-primary;
}
background-color: transparent;
color: $color-primary;
}

View File

@ -1,120 +0,0 @@
import React from 'react';
import BaseMenu from '../base/component';
import styles from '../styles.scss';
import { defineMessages, injectIntl } from 'react-intl';
import DeviceSelector from '/imports/ui/components/audio/device-selector/component';
import AudioStreamVolume from '/imports/ui/components/audio/audio-stream-volume/component';
import AudioTestContainer from '/imports/ui/components/audio-test/container';
import cx from 'classnames';
const intlMessages = defineMessages({
micSource: {
id: 'app.submenu.audio.micSourceLabel',
description: 'Label for microphone source',
},
speakerSource: {
id: 'app.submenu.audio.speakerSourceLabel',
description: 'Label for speaker source',
},
streamVolume: {
id: 'app.submenu.audio.streamVolumeLabel',
description: 'Label for stream volume',
},
});
class AudioMenu extends BaseMenu {
constructor(props) {
super(props);
this.handleInputChange = this.handleInputChange.bind(this);
this.handleOutputChange = this.handleOutputChange.bind(this);
this.state = {
settingsName: 'audio',
settings: props.settings,
};
}
chooseAudio() {
this.props.changeMenu(this.props.JOIN_AUDIO);
}
handleSelectChange(fieldname, options, e) {
let obj = this.state;
obj.settings[fieldname] = options[e.target.value];
this.setState(obj);
this.handleUpdateSettings('audio', obj);
}
handleInputChange(deviceId) {
let obj = this.state;
obj.settings.inputDeviceId = deviceId;
this.setState(obj);
this.handleUpdateSettings('audio', obj);
}
handleOutputChange(deviceId) {
let obj = this.state;
obj.settings.outputDeviceId = deviceId;
this.setState(obj);
this.handleUpdateSettings('audio', obj);
}
render() {
const { intl } = this.props;
return (
<div>
<div className={styles.header}>
<h3 className={styles.title}>Audio</h3>
</div>
<div className={styles.form}>
<div className={styles.row}>
<div className={styles.col}>
<div className={styles.formElement}>
<label className={cx(styles.label, styles.labelSmall)}>
{intl.formatMessage(intlMessages.micSource)}
</label>
<DeviceSelector
value={this.state.inputDeviceId}
className={styles.select}
kind="audioinput"
onChange={this.handleInputChange} />
</div>
</div>
<div className={styles.col}>
<div className={styles.formElement}>
<label className={cx(styles.label, styles.labelSmall)}>
{intl.formatMessage(intlMessages.streamVolume)}
</label>
<AudioStreamVolume
deviceId={this.state.inputDeviceId}
className={styles.audioMeter} />
</div>
</div>
</div>
<div className={styles.row}>
<div className={styles.col}>
<div className={styles.formElement}>
<label className={cx(styles.label, styles.labelSmall)}>
{intl.formatMessage(intlMessages.speakerSource)}
</label>
<DeviceSelector
value={this.state.outputDeviceId}
className={styles.select}
kind="audiooutput"
onChange={this.handleOutputChange} />
</div>
</div>
<div className={styles.col}>
<label className={styles.label}>&nbsp;</label>
<AudioTestContainer/>
</div>
</div>
</div>
</div>
);
}
};
export default injectIntl(AudioMenu);

View File

@ -127,7 +127,7 @@ class ClosedCaptionsMenu extends BaseMenu {
render() {
const {
locales,
intl
intl,
} = this.props;
return (

View File

@ -18,7 +18,7 @@ getClosedCaptionSettings = () => {
ccSettings.locales = locales;
return ccSettings;
}
};
export default {
getClosedCaptionSettings,

View File

@ -62,10 +62,6 @@
height: 1.75rem;
}
.audioMeter {
width: 100%;
}
.pullContentRight {
display: flex;
justify-content: flex-end;

View File

@ -32,7 +32,7 @@ const intlMessages = defineMessages({
},
});
class AudioMenu extends BaseMenu {
class VideoMenu extends BaseMenu {
constructor(props) {
super(props);
@ -101,4 +101,4 @@ class AudioMenu extends BaseMenu {
}
};
export default injectIntl(AudioMenu);
export default injectIntl(VideoMenu);

View File

@ -38,7 +38,8 @@ export default class Switch extends Toggle {
<input
{...inputProps}
ref={ref => { this.input = ref }}
ref={ref => { this.input = ref; }}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
className='react-toggle-screenreader-only'