Fix Recording regression caused by 'React Toolbar'
Saúl Ibarra Corretgé reported that Recording shows an error dialog
stating "There was an error connecting to your camera". Hristo Terezov
and Yana Stamcheva traced that the problem originates in
da4425b5c0
and, more specifically, is caused by a different order of execution due
to the move of the invocation of the function Recording.init.
The solution is to bring back the execution location of Recording.init.
This commit is contained in:
parent
0316450ee2
commit
ae06a6ce41
|
@ -322,6 +322,10 @@ UI.start = function () {
|
|||
|
||||
$("#videoconference_page").mousemove(debouncedShowToolbar);
|
||||
|
||||
// Initialise the recording module.
|
||||
if (config.enableRecording) {
|
||||
Recording.init(eventEmitter, config.recordingType);
|
||||
}
|
||||
// Initialize side panels
|
||||
SidePanels.init(eventEmitter);
|
||||
} else {
|
||||
|
|
|
@ -21,7 +21,7 @@ import UIUtil from '../util/UIUtil';
|
|||
import VideoLayout from '../videolayout/VideoLayout';
|
||||
import Feedback from '../feedback/Feedback.js';
|
||||
|
||||
import { hideToolbox } from '../../../react/features/toolbox';
|
||||
import { setToolboxEnabled } from '../../../react/features/toolbox';
|
||||
|
||||
/**
|
||||
* The dialog for user input.
|
||||
|
@ -35,8 +35,10 @@ let dialog = null;
|
|||
* @private
|
||||
*/
|
||||
function _isRecordingButtonEnabled() {
|
||||
return interfaceConfig.TOOLBAR_BUTTONS.indexOf("recording") !== -1
|
||||
&& config.enableRecording && APP.conference.isRecordingSupported();
|
||||
return (
|
||||
interfaceConfig.TOOLBAR_BUTTONS.indexOf("recording") !== -1
|
||||
&& config.enableRecording
|
||||
&& APP.conference.isRecordingSupported());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -179,19 +181,13 @@ function _showStopRecordingPrompt (recordingType) {
|
|||
buttonKey = "dialog.stopRecording";
|
||||
}
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
return new Promise((resolve, reject) => {
|
||||
dialog = APP.UI.messageHandler.openTwoButtonDialog({
|
||||
titleKey: title,
|
||||
msgKey: message,
|
||||
leftButtonKey: buttonKey,
|
||||
submitFunction: function(e,v) {
|
||||
if (v) {
|
||||
resolve();
|
||||
} else {
|
||||
reject();
|
||||
}
|
||||
},
|
||||
closeFunction: function () {
|
||||
submitFunction: (e, v) => (v ? resolve : reject)(),
|
||||
closeFunction: () => {
|
||||
dialog = null;
|
||||
}
|
||||
});
|
||||
|
@ -250,35 +246,12 @@ var Recording = {
|
|||
/**
|
||||
* Initializes the recording UI.
|
||||
*/
|
||||
init (emitter, recordingType) {
|
||||
this.eventEmitter = emitter;
|
||||
init(eventEmitter, recordingType) {
|
||||
this.eventEmitter = eventEmitter;
|
||||
this.recordingType = recordingType;
|
||||
|
||||
this.updateRecordingState(APP.conference.getRecordingState());
|
||||
|
||||
this.initRecordingButton(recordingType);
|
||||
|
||||
// If I am a recorder then I publish my recorder custom role to notify
|
||||
// everyone.
|
||||
if (config.iAmRecorder) {
|
||||
VideoLayout.enableDeviceAvailabilityIcons(
|
||||
APP.conference.getMyUserId(), false);
|
||||
VideoLayout.setLocalVideoVisible(false);
|
||||
Feedback.enableFeedback(false);
|
||||
APP.store.dispatch(hideToolbox());
|
||||
APP.UI.messageHandler.enableNotifications(false);
|
||||
APP.UI.messageHandler.enablePopups(false);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialise the recording button.
|
||||
*/
|
||||
initRecordingButton(recordingType) {
|
||||
let selector = $('#toolbar_button_record');
|
||||
|
||||
let button = selector.get(0);
|
||||
UIUtil.setTooltip(button, 'liveStreaming.buttonTooltip', 'right');
|
||||
|
||||
if (recordingType === 'jibri') {
|
||||
this.baseClass = "fa fa-play-circle";
|
||||
this.recordingTitle = "dialog.liveStreaming";
|
||||
|
@ -304,94 +277,37 @@ var Recording = {
|
|||
this.recordingBusy = "liveStreaming.busy";
|
||||
}
|
||||
|
||||
// XXX Due to the React-ification of Toolbox, the HTMLElement with id
|
||||
// toolbar_button_record may not exist yet.
|
||||
$(document).on(
|
||||
'click',
|
||||
'#toolbar_button_record',
|
||||
ev => this._onToolbarButtonClick(ev));
|
||||
|
||||
// If I am a recorder then I publish my recorder custom role to notify
|
||||
// everyone.
|
||||
if (config.iAmRecorder) {
|
||||
VideoLayout.enableDeviceAvailabilityIcons(
|
||||
APP.conference.getMyUserId(), false);
|
||||
VideoLayout.setLocalVideoVisible(false);
|
||||
Feedback.enableFeedback(false);
|
||||
APP.store.dispatch(setToolboxEnabled(false));
|
||||
APP.UI.messageHandler.enableNotifications(false);
|
||||
APP.UI.messageHandler.enablePopups(false);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialise the recording button.
|
||||
*/
|
||||
initRecordingButton() {
|
||||
const selector = $('#toolbar_button_record');
|
||||
|
||||
UIUtil.setTooltip(selector, 'liveStreaming.buttonTooltip', 'right');
|
||||
|
||||
selector.addClass(this.baseClass);
|
||||
selector.attr("data-i18n", "[content]" + this.recordingButtonTooltip);
|
||||
APP.translation.translateElement(selector);
|
||||
|
||||
var self = this;
|
||||
selector.click(function () {
|
||||
if (dialog)
|
||||
return;
|
||||
JitsiMeetJS.analytics.sendEvent('recording.clicked');
|
||||
switch (self.currentState) {
|
||||
case Status.ON:
|
||||
case Status.RETRYING:
|
||||
case Status.PENDING: {
|
||||
_showStopRecordingPrompt(recordingType).then(
|
||||
() => {
|
||||
self.eventEmitter.emit(UIEvents.RECORDING_TOGGLED);
|
||||
JitsiMeetJS.analytics.sendEvent(
|
||||
'recording.stopped');
|
||||
},
|
||||
() => {});
|
||||
break;
|
||||
}
|
||||
case Status.AVAILABLE:
|
||||
case Status.OFF: {
|
||||
if (recordingType === 'jibri')
|
||||
_requestLiveStreamId().then((streamId) => {
|
||||
self.eventEmitter.emit( UIEvents.RECORDING_TOGGLED,
|
||||
{streamId: streamId});
|
||||
JitsiMeetJS.analytics.sendEvent(
|
||||
'recording.started');
|
||||
}).catch(
|
||||
reason => {
|
||||
if (reason !== APP.UI.messageHandler.CANCEL)
|
||||
logger.error(reason);
|
||||
else
|
||||
JitsiMeetJS.analytics.sendEvent(
|
||||
'recording.canceled');
|
||||
}
|
||||
);
|
||||
else {
|
||||
if (self.predefinedToken) {
|
||||
self.eventEmitter.emit( UIEvents.RECORDING_TOGGLED,
|
||||
{token: self.predefinedToken});
|
||||
JitsiMeetJS.analytics.sendEvent(
|
||||
'recording.started');
|
||||
return;
|
||||
}
|
||||
|
||||
_requestRecordingToken().then((token) => {
|
||||
self.eventEmitter.emit( UIEvents.RECORDING_TOGGLED,
|
||||
{token: token});
|
||||
JitsiMeetJS.analytics.sendEvent(
|
||||
'recording.started');
|
||||
}).catch(
|
||||
reason => {
|
||||
if (reason !== APP.UI.messageHandler.CANCEL)
|
||||
logger.error(reason);
|
||||
else
|
||||
JitsiMeetJS.analytics.sendEvent(
|
||||
'recording.canceled');
|
||||
}
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Status.BUSY: {
|
||||
dialog = APP.UI.messageHandler.openMessageDialog(
|
||||
self.recordingTitle,
|
||||
self.recordingBusy,
|
||||
null,
|
||||
function () {
|
||||
dialog = null;
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
dialog = APP.UI.messageHandler.openMessageDialog(
|
||||
self.recordingTitle,
|
||||
self.recordingUnavailable,
|
||||
null,
|
||||
function () {
|
||||
dialog = null;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -514,6 +430,90 @@ var Recording = {
|
|||
APP.translation.translateElement(labelSelector);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles {@code click} on {@code toolbar_button_record}.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onToolbarButtonClick() {
|
||||
if (dialog) {
|
||||
return;
|
||||
}
|
||||
|
||||
JitsiMeetJS.analytics.sendEvent('recording.clicked');
|
||||
switch (this.currentState) {
|
||||
case Status.ON:
|
||||
case Status.RETRYING:
|
||||
case Status.PENDING: {
|
||||
_showStopRecordingPrompt(this.recordingType).then(
|
||||
() => {
|
||||
this.eventEmitter.emit(UIEvents.RECORDING_TOGGLED);
|
||||
JitsiMeetJS.analytics.sendEvent('recording.stopped');
|
||||
},
|
||||
() => {});
|
||||
break;
|
||||
}
|
||||
case Status.AVAILABLE:
|
||||
case Status.OFF: {
|
||||
if (this.recordingType === 'jibri')
|
||||
_requestLiveStreamId().then(streamId => {
|
||||
this.eventEmitter.emit(
|
||||
UIEvents.RECORDING_TOGGLED,
|
||||
{ streamId });
|
||||
JitsiMeetJS.analytics.sendEvent('recording.started');
|
||||
}).catch(reason => {
|
||||
if (reason !== APP.UI.messageHandler.CANCEL)
|
||||
logger.error(reason);
|
||||
else
|
||||
JitsiMeetJS.analytics.sendEvent('recording.canceled');
|
||||
});
|
||||
else {
|
||||
if (this.predefinedToken) {
|
||||
this.eventEmitter.emit(
|
||||
UIEvents.RECORDING_TOGGLED,
|
||||
{ token: this.predefinedToken });
|
||||
JitsiMeetJS.analytics.sendEvent('recording.started');
|
||||
return;
|
||||
}
|
||||
|
||||
_requestRecordingToken().then((token) => {
|
||||
this.eventEmitter.emit(
|
||||
UIEvents.RECORDING_TOGGLED,
|
||||
{ token });
|
||||
JitsiMeetJS.analytics.sendEvent('recording.started');
|
||||
}).catch(reason => {
|
||||
if (reason !== APP.UI.messageHandler.CANCEL)
|
||||
logger.error(reason);
|
||||
else
|
||||
JitsiMeetJS.analytics.sendEvent('recording.canceled');
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Status.BUSY: {
|
||||
dialog = APP.UI.messageHandler.openMessageDialog(
|
||||
this.recordingTitle,
|
||||
this.recordingBusy,
|
||||
null,
|
||||
() => {
|
||||
dialog = null;
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
dialog = APP.UI.messageHandler.openMessageDialog(
|
||||
this.recordingTitle,
|
||||
this.recordingUnavailable,
|
||||
null,
|
||||
() => {
|
||||
dialog = null;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the toggled state of the recording toolbar button.
|
||||
*
|
||||
|
|
|
@ -157,11 +157,13 @@ const IndicatorFontSizes = {
|
|||
* @param position the position of the tooltip in relation to the element
|
||||
*/
|
||||
setTooltip(element, key, position) {
|
||||
if (element !== null) {
|
||||
element.setAttribute('data-tooltip', TOOLTIP_POSITIONS[position]);
|
||||
element.setAttribute('data-i18n', '[content]' + key);
|
||||
if (element) {
|
||||
const selector = element.jquery ? element : $(element);
|
||||
|
||||
APP.translation.translateElement($(element));
|
||||
selector.attr('data-tooltip', TOOLTIP_POSITIONS[position]);
|
||||
selector.attr('data-i18n', `[content]${key}`);
|
||||
|
||||
APP.translation.translateElement(selector);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -21,16 +21,6 @@ export const CLEAR_TOOLBOX_TIMEOUT = Symbol('CLEAR_TOOLBOX_TIMEOUT');
|
|||
export const SET_DEFAULT_TOOLBOX_BUTTONS
|
||||
= Symbol('SET_DEFAULT_TOOLBOX_BUTTONS');
|
||||
|
||||
/**
|
||||
* The type of the action which sets the permanent visibility of the Toolbox.
|
||||
*
|
||||
* {
|
||||
* type: SET_TOOLBOX_ALWAYS_VISIBLE,
|
||||
* alwaysVisible: boolean
|
||||
* }
|
||||
*/
|
||||
export const SET_TOOLBOX_ALWAYS_VISIBLE = Symbol('SET_TOOLBOX_ALWAYS_VISIBLE');
|
||||
|
||||
/**
|
||||
* The type of the action which sets the conference subject.
|
||||
*
|
||||
|
@ -73,6 +63,26 @@ export const SET_TOOLBAR_BUTTON = Symbol('SET_TOOLBAR_BUTTON');
|
|||
*/
|
||||
export const SET_TOOLBAR_HOVERED = Symbol('SET_TOOLBAR_HOVERED');
|
||||
|
||||
/**
|
||||
* The type of the action which sets the permanent visibility of the Toolbox.
|
||||
*
|
||||
* {
|
||||
* type: SET_TOOLBOX_ALWAYS_VISIBLE,
|
||||
* alwaysVisible: boolean
|
||||
* }
|
||||
*/
|
||||
export const SET_TOOLBOX_ALWAYS_VISIBLE = Symbol('SET_TOOLBOX_ALWAYS_VISIBLE');
|
||||
|
||||
/**
|
||||
* The type of the (redux) action which enables/disables the Toolbox.
|
||||
*
|
||||
* {
|
||||
* type: SET_TOOLBOX_ENABLED,
|
||||
* enabled: boolean
|
||||
* }
|
||||
*/
|
||||
export const SET_TOOLBOX_ENABLED = Symbol('SET_TOOLBOX_ENABLED');
|
||||
|
||||
/**
|
||||
* The type of the action which sets a new Toolbox visibility timeout and its
|
||||
* delay.
|
||||
|
|
|
@ -5,11 +5,12 @@ import type { Dispatch } from 'redux-thunk';
|
|||
import {
|
||||
CLEAR_TOOLBOX_TIMEOUT,
|
||||
SET_DEFAULT_TOOLBOX_BUTTONS,
|
||||
SET_TOOLBOX_ALWAYS_VISIBLE,
|
||||
SET_SUBJECT,
|
||||
SET_SUBJECT_SLIDE_IN,
|
||||
SET_TOOLBAR_BUTTON,
|
||||
SET_TOOLBAR_HOVERED,
|
||||
SET_TOOLBOX_ALWAYS_VISIBLE,
|
||||
SET_TOOLBOX_ENABLED,
|
||||
SET_TOOLBOX_TIMEOUT,
|
||||
SET_TOOLBOX_TIMEOUT_MS,
|
||||
SET_TOOLBOX_VISIBLE
|
||||
|
@ -168,6 +169,22 @@ export function setToolboxAlwaysVisible(alwaysVisible: boolean): Object {
|
|||
|
||||
/* eslint-disable flowtype/space-before-type-colon */
|
||||
|
||||
/**
|
||||
* Enables/disables the toolbox.
|
||||
*
|
||||
* @param {boolean} enabled - True to enable the toolbox or false to disable it.
|
||||
* @returns {{
|
||||
* type: SET_TOOLBOX_ENABLED,
|
||||
* enabled: boolean
|
||||
* }}
|
||||
*/
|
||||
export function setToolboxEnabled(enabled: boolean): Object {
|
||||
return {
|
||||
type: SET_TOOLBOX_ENABLED,
|
||||
enabled
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches an action which sets new timeout and clears the previous one.
|
||||
*
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
import Recording from '../../../modules/UI/recording/Recording';
|
||||
import SideContainerToggler
|
||||
from '../../../modules/UI/side_pannels/SideContainerToggler';
|
||||
import UIEvents from '../../../service/UI/UIEvents';
|
||||
import UIUtil from '../../../modules/UI/util/UIUtil';
|
||||
import UIEvents from '../../../service/UI/UIEvents';
|
||||
|
||||
import {
|
||||
clearToolboxTimeout,
|
||||
|
@ -171,14 +171,11 @@ export function showDialPadButton(show: boolean): Function {
|
|||
*/
|
||||
export function showRecordingButton(): Function {
|
||||
return (dispatch: Dispatch<*>) => {
|
||||
const eventEmitter = APP.UI.eventEmitter;
|
||||
const buttonName = 'recording';
|
||||
|
||||
dispatch(setToolbarButton(buttonName, {
|
||||
dispatch(setToolbarButton('recording', {
|
||||
hidden: false
|
||||
}));
|
||||
|
||||
Recording.init(eventEmitter, config.recordingType);
|
||||
Recording.initRecordingButton();
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -234,9 +231,14 @@ export function showSIPCallButton(show: boolean): Function {
|
|||
export function showToolbox(timeout: number = 0): Object {
|
||||
return (dispatch: Dispatch<*>, getState: Function) => {
|
||||
const state = getState();
|
||||
const { alwaysVisible, timeoutMS, visible } = state['features/toolbox'];
|
||||
const {
|
||||
alwaysVisible,
|
||||
enabled,
|
||||
timeoutMS,
|
||||
visible
|
||||
} = state['features/toolbox'];
|
||||
|
||||
if (!visible) {
|
||||
if (enabled && !visible) {
|
||||
dispatch(setToolboxVisible(true));
|
||||
dispatch(setSubjectSlideIn(true));
|
||||
|
||||
|
|
|
@ -5,11 +5,12 @@ import { ReducerRegistry } from '../base/redux';
|
|||
import {
|
||||
CLEAR_TOOLBOX_TIMEOUT,
|
||||
SET_DEFAULT_TOOLBOX_BUTTONS,
|
||||
SET_TOOLBOX_ALWAYS_VISIBLE,
|
||||
SET_SUBJECT,
|
||||
SET_SUBJECT_SLIDE_IN,
|
||||
SET_TOOLBAR_BUTTON,
|
||||
SET_TOOLBAR_HOVERED,
|
||||
SET_TOOLBOX_ALWAYS_VISIBLE,
|
||||
SET_TOOLBOX_ENABLED,
|
||||
SET_TOOLBOX_TIMEOUT,
|
||||
SET_TOOLBOX_TIMEOUT_MS,
|
||||
SET_TOOLBOX_VISIBLE
|
||||
|
@ -51,6 +52,14 @@ function _getInitialState() {
|
|||
*/
|
||||
alwaysVisible: false,
|
||||
|
||||
/**
|
||||
* The indicator which determines whether the Toolbox is enabled. For
|
||||
* example, modules/UI/recording/Recording.js disables the Toolbox.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
enabled: true,
|
||||
|
||||
/**
|
||||
* The indicator which determines whether a Toolbar in the Toolbox is
|
||||
* hovered.
|
||||
|
@ -132,12 +141,6 @@ ReducerRegistry.register(
|
|||
};
|
||||
}
|
||||
|
||||
case SET_TOOLBOX_ALWAYS_VISIBLE:
|
||||
return {
|
||||
...state,
|
||||
alwaysVisible: action.alwaysVisible
|
||||
};
|
||||
|
||||
case SET_SUBJECT:
|
||||
return {
|
||||
...state,
|
||||
|
@ -159,6 +162,18 @@ ReducerRegistry.register(
|
|||
hovered: action.hovered
|
||||
};
|
||||
|
||||
case SET_TOOLBOX_ALWAYS_VISIBLE:
|
||||
return {
|
||||
...state,
|
||||
alwaysVisible: action.alwaysVisible
|
||||
};
|
||||
|
||||
case SET_TOOLBOX_ENABLED:
|
||||
return {
|
||||
...state,
|
||||
enabled: action.enabled
|
||||
};
|
||||
|
||||
case SET_TOOLBOX_TIMEOUT:
|
||||
return {
|
||||
...state,
|
||||
|
|
Loading…
Reference in New Issue