diff --git a/bigbluebutton-html5/imports/api/audio/client/bridge/base.js b/bigbluebutton-html5/imports/api/audio/client/bridge/base.js index 5ce1ab1a76bc694ff33800151ca03c02119abb93..2eba9e32724654ec7883f618663b5db991c28d9c 100644 --- a/bigbluebutton-html5/imports/api/audio/client/bridge/base.js +++ b/bigbluebutton-html5/imports/api/audio/client/bridge/base.js @@ -3,10 +3,10 @@ export default class BaseAudioBridge { this.userData = userData; this.baseErrorCodes = { - INVALID_TARGET: 'Invalid Target', - CONNECTION_ERROR: 'Connection Error', - REQUEST_TIMEOUT: 'Request Timeout', - GENERIC_ERROR: 'An Error Occurred', + INVALID_TARGET: 'INVALID_TARGET', + CONNECTION_ERROR: 'CONNECTION_ERROR', + REQUEST_TIMEOUT: 'REQUEST_TIMEOUT', + GENERIC_ERROR: 'GENERIC_ERROR', }; this.baseCallStates = { @@ -23,4 +23,12 @@ export default class BaseAudioBridge { joinAudio() { console.error('The Bridge must implement joinAudio'); } + + changeInputDevice() { + console.error('The Bridge must implement changeInputDevice'); + } + + changeOutputDevice() { + console.error('The Bridge must implement changeOutputDevice'); + } } diff --git a/bigbluebutton-html5/imports/api/audio/client/bridge/sip.js b/bigbluebutton-html5/imports/api/audio/client/bridge/sip.js index e3bf03f8866591699996783e6f00f7f39bfd4519..c2cd5653a79637d2a3f2823f1626bafb1a1d269e 100644 --- a/bigbluebutton-html5/imports/api/audio/client/bridge/sip.js +++ b/bigbluebutton-html5/imports/api/audio/client/bridge/sip.js @@ -52,10 +52,11 @@ export default class SIPBridge extends BaseAudioBridge { this.protocol = window.document.location.protocol; this.hostname = window.document.location.hostname; + const causes = window.SIP.C.causes this.errorCodes = { - 'Request Timeout': this.baseErrorCodes.REQUEST_TIMEOUT, - 'Invalid Target': this.baseErrorCodes.INVALID_TARGET, - 'Connection Error': this.baseErrorCodes.CONNECTION_ERROR, + [causes.REQUEST_TIMEOUT]: this.baseErrorCodes.REQUEST_TIMEOUT, + [causes.INVALID_TARGET]: this.baseErrorCodes.INVALID_TARGET, + [causes.CONNECTION_ERROR]: this.baseErrorCodes.CONNECTION_ERROR, }; } @@ -316,7 +317,9 @@ export default class SIPBridge extends BaseAudioBridge { const audioContext = document.querySelector(MEDIA_TAG); if (audioContext.setSinkId) { - audioContext.setSinkId(deviceId); + audioContext.setSinkId(value); } + + return value; } } diff --git a/bigbluebutton-html5/imports/ui/components/app/component.jsx b/bigbluebutton-html5/imports/ui/components/app/component.jsx index f3c0b2440aaf21e1820f893783ff4e0ecb4c4b82..0949efcc97f0d24dfda41c6a5ae5b06ac152fa9e 100755 --- a/bigbluebutton-html5/imports/ui/components/app/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/app/component.jsx @@ -7,7 +7,6 @@ import cx from 'classnames'; import ToastContainer from '../toast/container'; import ModalContainer from '../modal/container'; import NotificationsBarContainer from '../notifications-bar/container'; -import AudioNotificationContainer from '../audio/audio-notification/container'; import AudioContainer from '../audio/container'; import ChatNotificationContainer from '../chat/notification/container'; import styles from './styles'; @@ -176,7 +175,6 @@ class App extends Component { return ( <main className={styles.main}> - <AudioNotificationContainer /> <NotificationsBarContainer /> <section className={styles.wrapper}> {this.renderUserList()} diff --git a/bigbluebutton-html5/imports/ui/components/audio/container.jsx b/bigbluebutton-html5/imports/ui/components/audio/container.jsx index 0f4f5fc96dd2e3097b975af52798bcfd65edf2e9..afb3a6e08bbf037fbd00ff45a5eefec91ac65593 100644 --- a/bigbluebutton-html5/imports/ui/components/audio/container.jsx +++ b/bigbluebutton-html5/imports/ui/components/audio/container.jsx @@ -1,6 +1,7 @@ import React from 'react'; import { createContainer } from 'meteor/react-meteor-data'; import { withModalMounter } from '/imports/ui/components/modal/service'; +import { injectIntl, defineMessages } from 'react-intl'; import PropTypes from 'prop-types'; import Service from './service'; import Audio from './component'; @@ -14,6 +15,39 @@ const defaultProps = { children: null, }; +const intlMessages = defineMessages({ + joinedAudio: { + id: 'app.audioManager.joinedAudio', + description: 'Joined audio toast message', + }, + joinedEcho : { + id: 'app.audioManager.joinedEcho', + description: 'Joined echo test toast message', + }, + leftAudio: { + id: 'app.audioManager.leftAudio', + description: 'Left audio toast message', + }, + + genericError: { + id: 'app.audioManager.genericError', + description: 'Generic error messsage', + }, + connectionError: { + id: 'app.audioManager.connectionError', + description: 'Connection error messsage', + }, + requestTimeout: { + id: 'app.audioManager.requestTimeout', + description: 'Request timeout error messsage', + }, + invalidTarget: { + id: 'app.audioManager.invalidTarget', + description: 'Invalid target error messsage', + }, +}); + + const AudioContainer = props => (<Audio {...props}> {props.children} @@ -22,21 +56,35 @@ const AudioContainer = props => let didMountAutoJoin = false; -export default withModalMounter(createContainer(({ mountModal }) => { +export default withModalMounter(injectIntl(createContainer(({ mountModal, intl }) => { const APP_CONFIG = Meteor.settings.public.app; const { autoJoinAudio } = APP_CONFIG; + const messages = { + info: { + JOINED_AUDIO: intl.formatMessage(intlMessages.joinedAudio), + JOINED_ECHO: intl.formatMessage(intlMessages.joinedEcho), + LEFT_AUDIO: intl.formatMessage(intlMessages.leftAudio), + }, + error: { + GENERIC_ERROR: intl.formatMessage(intlMessages.genericError), + CONNECTION_ERROR: intl.formatMessage(intlMessages.connectionError), + REQUEST_TIMEOUT: intl.formatMessage(intlMessages.requestTimeout), + INVALID_TARGET: intl.formatMessage(intlMessages.invalidTarget), + }, + } + return { init: () => { - Service.init(); + Service.init(messages); Service.changeOutputDevice(document.querySelector('#remote-media').sinkId) if (!autoJoinAudio || didMountAutoJoin) return; mountModal(<AudioModalContainer />); didMountAutoJoin = true; }, }; -}, AudioContainer)); +}, AudioContainer))); AudioContainer.propTypes = propTypes; -AudioContainer.d +AudioContainer.defaultProps = defaultProps; diff --git a/bigbluebutton-html5/imports/ui/components/audio/service.js b/bigbluebutton-html5/imports/ui/components/audio/service.js index 10a222287fdc3e8b88df27e8f516053c1bae1233..1cac5d41cafc9d30e6da106571e75df141e19457 100644 --- a/bigbluebutton-html5/imports/ui/components/audio/service.js +++ b/bigbluebutton-html5/imports/ui/components/audio/service.js @@ -3,7 +3,7 @@ import Auth from '/imports/ui/services/auth'; import AudioManager from '/imports/ui/services/audio-manager'; import Meetings from '/imports/api/meetings'; -const init = () => { +const init = (messages) => { const meetingId = Auth.meetingID; const userId = Auth.userID; const sessionToken = Auth.sessionToken; @@ -24,7 +24,7 @@ const init = () => { microphoneLockEnforced, }; - AudioManager.init(userData); + AudioManager.init(userData, messages); }; export default { diff --git a/bigbluebutton-html5/imports/ui/services/audio-manager/index.js b/bigbluebutton-html5/imports/ui/services/audio-manager/index.js index 2086e464af7df9c188c7f72c2d74252879c37d57..1e3c2d4868eec62d9bb2383c9638f29f6d955e8f 100644 --- a/bigbluebutton-html5/imports/ui/services/audio-manager/index.js +++ b/bigbluebutton-html5/imports/ui/services/audio-manager/index.js @@ -2,6 +2,7 @@ import { Tracker } from 'meteor/tracker'; import { makeCall } from '/imports/ui/services/api'; import VertoBridge from '/imports/api/audio/client/bridge/verto'; import SIPBridge from '/imports/api/audio/client/bridge/sip'; +import notify from '/imports/ui/components/toast/service'; const MEDIA = Meteor.settings.public.media; const USE_SIP = MEDIA.useSIPAudio; @@ -30,10 +31,10 @@ class AudioManager { }); } - init(userData) { + init(userData, messages) { this.bridge = USE_SIP ? new SIPBridge(userData) : new VertoBridge(userData); this.userData = userData; - + this.messages = messages; this.changeInputDevice(); } @@ -58,7 +59,7 @@ class AudioManager { }); } - joinAudio(options = {}, callbacks = {}) { + joinAudio(options = {}) { const { isListenOnly, isEchoTest, @@ -69,7 +70,6 @@ class AudioManager { this.error = null; this.isListenOnly = isListenOnly || false; this.isEchoTest = isEchoTest || false; - this.callbacks = callbacks; const callOptions = { isListenOnly: this.isListenOnly, @@ -96,11 +96,13 @@ class AudioManager { } onAudioJoin() { - if (!this.isEchoTest) { - this.isConnected = true; - } - this.isConnecting = false; + if (this.isEchoTest) { + return; + }1 + + notify(this.messages.info.JOINED_AUDIO, 'info', 'audio_on'); + this.isConnected = true; } onTransferStart() { @@ -114,7 +116,14 @@ class AudioManager { if (this.isEchoTest) { this.isEchoTest = false; + return; + } + + if (this.error) { + return; } + + notify(this.messages.info.LEFT_AUDIO, 'info', 'audio_on'); } onToggleMicrophoneMute() { @@ -141,6 +150,7 @@ class AudioManager { this.onAudioExit(); } else if (status === FAILED) { this.error = error; + notify(this.messages.error[error], 'error', 'audio_on'); this.onAudioExit(); } }); @@ -159,8 +169,7 @@ class AudioManager { } async changeInputDevice(deviceId) { - const device = await this.bridge.changeInputDevice(deviceId); - this.inputDevice = device; + this.inputDevice = await this.bridge.changeInputDevice(deviceId); } async changeOutputDevice(deviceId) { diff --git a/bigbluebutton-html5/private/locales/en.json b/bigbluebutton-html5/private/locales/en.json index 04d8987eeb54744d3cba20e627eb528dad5404ad..6a4d5e659389af94a563c88f57f8c52e5fcdb6dd 100644 --- a/bigbluebutton-html5/private/locales/en.json +++ b/bigbluebutton-html5/private/locales/en.json @@ -204,10 +204,19 @@ "app.audioModal.closeLabel": "Close", "app.audioModal.yes": "Yes", "app.audioModal.no": "No", - "app.audioModal.echoTestTitle": "Speak a few words, Do you hear yourself?", + "app.audioModal.echoTestTitle": "Speak a few words, Do you hear yourself? Does everything seems correct?", "app.audioModal.settingsTitle": "Change your audio settings", "app.audioModal.connecting": "Connecting", "app.audioModal.connectingEchoTest": "Connecting to echo test", + + "app.audioManager.joinedAudio": "You have joined the audio conference", + "app.audioManager.joinedEcho": "You have joined the echo test", + "app.audioManager.leftAudio": "You have left the audio conference", + "app.audioManager.genericError": "Error: An error has occurred, please try again", + "app.audioManager.connectionError": "Error: Connection error", + "app.audioManager.requestTimeout": "Error: There was a timeout in the request", + "app.audioManager.invalidTarget": "Error: Tried to request something to an invalid target", + "app.audio.joinAudio": "Join Audio", "app.audio.leaveAudio": "Leave Audio", "app.audio.enterSessionLabel": "Enter Session", @@ -227,7 +236,7 @@ "app.error.500": "Ops, something went wrong", "app.error.404": "Not found", "app.error.401": "Unauthorized", - "app.error.403": "Forbidden", - "app.error.leaveLabel": "Log in again", + "app.error.403": "Forbidden", + "app.error.leaveLabel": "Log in again", "app.guest.waiting": "Waiting for approval to join" }