feat(overlays): CallOverlay is not really an overlay

It's not supposed to go on top of everything, like an error would. It's tied to
a conference, so render it outside of the OverlayContainer.
This commit is contained in:
Saúl Ibarra Corretgé 2017-11-23 17:26:47 +01:00 committed by Lyubo Marinov
parent b39b6640b4
commit 0f6243ee88
14 changed files with 185 additions and 134 deletions

View File

@ -1,12 +1,12 @@
/**
* The type of redux action which sets the visibility of {@code CallOverlay}.
* The type of redux action which sets the visibility of {@code CalleeInfo}.
*
* {
* type: SET_CALL_OVERLAY_VISIBLE,
* callOverlayVisible: boolean
* type: SET_CALLEE_INFO_VISIBLE,
* calleeInfoVisible: boolean
* }
*/
export const SET_CALL_OVERLAY_VISIBLE = Symbol('SET_CALL_OVERLAY_VISIBLE');
export const SET_CALLEE_INFO_VISIBLE = Symbol('SET_CALLEE_INFO_VISIBLE');
/**
* The type of redux action which stores a specific JSON Web Token (JWT) into

View File

@ -1,25 +1,25 @@
// @flow
import { SET_CALL_OVERLAY_VISIBLE, SET_JWT } from './actionTypes';
import { SET_CALLEE_INFO_VISIBLE, SET_JWT } from './actionTypes';
/**
* Sets the visibility of {@code CallOverlay}.
* Sets the visibility of {@code CalleeInfo}.
*
* @param {boolean|undefined} [callOverlayVisible] - If {@code CallOverlay} is
* @param {boolean|undefined} [calleeInfoVisible] - If {@code CalleeInfo} is
* to be displayed/visible, then {@code true}; otherwise, {@code false} or
* {@code undefined}.
* @returns {{
* type: SET_CALL_OVERLAY_VISIBLE,
* callOverlayVisible: (boolean|undefined)
* type: SET_CALLEE_INFO_VISIBLE,
* calleeInfoVisible: (boolean|undefined)
* }}
*/
export function setCallOverlayVisible(callOverlayVisible: ?boolean) {
export function setCalleeInfoVisible(calleeInfoVisible: ?boolean) {
return (dispatch: Dispatch<*>, getState: Function) => {
getState()['features/base/jwt']
.callOverlayVisible === callOverlayVisible
.calleeInfoVisible === calleeInfoVisible
|| dispatch({
type: SET_CALL_OVERLAY_VISIBLE,
callOverlayVisible
type: SET_CALLEE_INFO_VISIBLE,
calleeInfoVisible
});
};
}

View File

@ -1,6 +1,5 @@
// @flow
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
@ -15,82 +14,75 @@ declare var $: Object;
declare var APP: Object;
declare var interfaceConfig: Object;
/**
* The type of the React {@code Component} props of {@link CalleeInfo}.
*/
type Props = {
/**
* Object containing the callee's information.
*/
_callee: Object
}
/**
* The type of the React {@code Component} state of {@link CalleeInfo}.
*/
type State = {
/**
* The CSS class (name), if any, to add to this {@code CalleeInfo}.
*
* @type {string}
*/
className: ?string,
/**
* The indicator which determines whether this {@code CalleeInfo}
* should play/render audio to indicate the ringing phase of the
* call establishment between the local participant and the
* associated remote callee.
*
* @type {boolean}
*/
renderAudio: boolean,
/**
* The indicator which determines whether this {@code CalleeInfo}
* is depicting the ringing phase of the call establishment between
* the local participant and the associated remote callee or the
* phase afterwards when the callee has not answered the call for a
* period of time and, consequently, is considered unavailable.
*
* @type {boolean}
*/
ringing: boolean
}
/**
* Implements a React {@link Component} which depicts the establishment of a
* call with a specific remote callee.
*
* @extends Component
*/
class CallOverlay extends Component<*, *> {
/**
* {@code CallOverlay} component's property types.
*
* @static
*/
static propTypes = {
_callee: PropTypes.object
};
/**
* Determines whether this overlay needs to be rendered (according to a
* specific redux state). Called by {@link OverlayContainer}.
*
* @param {Object} state - The redux state.
* @returns {boolean} - If this overlay needs to be rendered, {@code true};
* {@code false}, otherwise.
*/
static needsRender(state) {
return state['features/base/jwt'].callOverlayVisible;
}
class CalleeInfo extends Component<Props, State> {
/**
* The (reference to the) {@link Audio} which plays/renders the audio
* depicting the ringing phase of the call establishment represented by this
* {@code CallOverlay}.
* {@code CalleeInfo}.
*/
_audio: ?Audio
_audio: ?Audio;
_onLargeVideoAvatarVisible: Function
_onLargeVideoAvatarVisible: Function;
_playAudioInterval: ?number
_playAudioInterval: ?number;
_ringingTimeout: ?number
_setAudio: Function
state: {
/**
* The CSS class (name), if any, to add to this {@code CallOverlay}.
*
* @type {string}
*/
className: ?string,
/**
* The indicator which determines whether this {@code CallOverlay}
* should play/render audio to indicate the ringing phase of the
* call establishment between the local participant and the
* associated remote callee.
*
* @type {boolean}
*/
renderAudio: boolean,
/**
* The indicator which determines whether this {@code CallOverlay}
* is depicting the ringing phase of the call establishment between
* the local participant and the associated remote callee or the
* phase afterwards when the callee has not answered the call for a
* period of time and, consequently, is considered unavailable.
*
* @type {boolean}
*/
ringing: boolean
}
_setAudio: Function;
/**
* Initializes a new {@code CallOverlay} instance.
* Initializes a new {@code CalleeInfo} instance.
*
* @param {Object} props - The read-only React {@link Component} props with
* which the new instance is to be initialized.
@ -119,13 +111,13 @@ class CallOverlay extends Component<*, *> {
/**
* Sets up timeouts such as the timeout to end the ringing phase of the call
* establishment depicted by this {@code CallOverlay}.
* establishment depicted by this {@code CalleeInfo}.
*
* @inheritdoc
*/
componentDidMount() {
// Set up the timeout to end the ringing phase of the call establishment
// depicted by this CallOverlay.
// depicted by this CalleeInfo.
if (this.state.ringing && !this._ringingTimeout) {
this._ringingTimeout
= setTimeout(
@ -200,7 +192,7 @@ class CallOverlay extends Component<*, *> {
}
/**
* Notifies this {@code CallOverlay} that the visibility of the
* Notifies this {@code CalleeInfo} that the visibility of the
* participant's avatar in the large video has changed.
*
* @param {boolean} largeVideoAvatarVisible - If the avatar in the large
@ -217,7 +209,7 @@ class CallOverlay extends Component<*, *> {
/**
* Stops the playback of the audio which represents the ringing phase of the
* call establishment depicted by this {@code CallOverlay}.
* call establishment depicted by this {@code CalleeInfo}.
*
* @private
* @returns {void}
@ -236,7 +228,7 @@ class CallOverlay extends Component<*, *> {
/**
* Starts the playback of the audio which represents the ringing phase of
* the call establishment depicted by this {@code CallOverlay}.
* the call establishment depicted by this {@code CalleeInfo}.
*
* @private
* @returns {void}
@ -253,7 +245,7 @@ class CallOverlay extends Component<*, *> {
/**
* Renders an audio element to represent the ringing phase of the call
* establishment represented by this {@code CallOverlay}.
* establishment represented by this {@code CalleeInfo}.
*
* @private
* @returns {ReactElement}
@ -272,11 +264,11 @@ class CallOverlay extends Component<*, *> {
/**
* Sets the (reference to the) {@link Audio} which renders the ringing phase
* of the call establishment represented by this {@code CallOverlay}.
* of the call establishment represented by this {@code CalleeInfo}.
*
* @param {Audio} audio - The (reference to the) {@code Audio} which
* plays/renders the audio depicting the ringing phase of the call
* establishment represented by this {@code CallOverlay}.
* establishment represented by this {@code CalleeInfo}.
* @private
* @returns {void}
*/
@ -332,7 +324,7 @@ class CallOverlay extends Component<*, *> {
}
/**
* Maps (parts of) the redux state to {@code CallOverlay}'s props.
* Maps (parts of) the redux state to {@code CalleeInfo}'s props.
*
* @param {Object} state - The redux state.
* @private
@ -351,4 +343,4 @@ function _mapStateToProps(state) {
};
}
export default connect(_mapStateToProps)(CallOverlay);
export default connect(_mapStateToProps)(CalleeInfo);

View File

@ -1 +1 @@
export { default as CallOverlay } from './CallOverlay';
export { default as CalleeInfo } from './CalleeInfo';

View File

@ -5,7 +5,7 @@ export default createStyleSheet({
// with the existing CSS class names on Web.
/**
* The style of {@code CallOverlay}.
* The style of {@code CalleeInfo}.
*/
ringing: {
alignItems: 'center',
@ -43,7 +43,7 @@ export default createStyleSheet({
},
/**
* The style of {@code Text} within {@code CallOverlay}.
* The style of {@code Text} within {@code CalleeInfo}.
*/
'ringing__text': {
color: ColorPalette.white

View File

@ -19,7 +19,7 @@ import {
} from '../participants';
import { MiddlewareRegistry } from '../redux';
import { setCallOverlayVisible, setJWT } from './actions';
import { setCalleeInfoVisible, setJWT } from './actions';
import { SET_JWT } from './actionTypes';
import { parseJWTFromURLParams } from './functions';
@ -40,7 +40,7 @@ MiddlewareRegistry.register(store => next => action => {
case LIB_INIT_ERROR:
case PARTICIPANT_JOINED:
case SET_ROOM:
return _maybeSetCallOverlayVisible(store, next, action);
return _maybeSetCalleeInfoVisible(store, next, action);
case SET_CONFIG:
case SET_LOCATION_URL:
@ -61,7 +61,7 @@ MiddlewareRegistry.register(store => next => action => {
/**
* Notifies the feature jwt that a specific {@code action} is being dispatched
* within a specific redux {@code store} which may have an effect on the
* visiblity of (the) {@code CallOverlay}.
* visiblity of (the) {@code CalleeInfo}.
*
* @param {Store} store - The redux store in which the specified {@code action}
* is being dispatched.
@ -73,17 +73,17 @@ MiddlewareRegistry.register(store => next => action => {
* @returns {Object} The new state that is the result of the reduction of the
* specified {@code action}.
*/
function _maybeSetCallOverlayVisible({ dispatch, getState }, next, action) {
function _maybeSetCalleeInfoVisible({ dispatch, getState }, next, action) {
const result = next(action);
const state = getState();
const stateFeaturesJWT = state['features/base/jwt'];
let callOverlayVisible;
let calleeInfoVisible;
if (stateFeaturesJWT.callee) {
const { conference, leaving, room } = state['features/base/conference'];
// XXX The CallOverlay is to be displayed/visible as soon as possible
// XXX The CalleeInfo is to be displayed/visible as soon as possible
// including even before the conference is joined.
if (room && (!conference || conference !== leaving)) {
switch (action.type) {
@ -91,7 +91,7 @@ function _maybeSetCallOverlayVisible({ dispatch, getState }, next, action) {
case CONFERENCE_LEFT:
case CONFERENCE_WILL_LEAVE:
case LIB_INIT_ERROR:
// Because the CallOverlay is to be displayed/visible as soon as
// Because the CalleeInfo is to be displayed/visible as soon as
// possible even before the connection is established and/or the
// conference is joined, it is very complicated to figure out
// based on the current state alone. In order to reduce the
@ -103,24 +103,24 @@ function _maybeSetCallOverlayVisible({ dispatch, getState }, next, action) {
break;
default: {
// The CallOverlay is to no longer be displayed/visible as soon
// The CalleeInfo is to no longer be displayed/visible as soon
// as another participant joins.
callOverlayVisible
calleeInfoVisible
= getParticipantCount(state) === 1
&& Boolean(getLocalParticipant(state));
// However, the CallDialog is not to be displayed/visible again
// after all remote participants leave.
if (callOverlayVisible
&& stateFeaturesJWT.callOverlayVisible === false) {
callOverlayVisible = false;
if (calleeInfoVisible
&& stateFeaturesJWT.calleeInfoVisible === false) {
calleeInfoVisible = false;
}
break;
}
}
}
}
dispatch(setCallOverlayVisible(callOverlayVisible));
dispatch(setCalleeInfoVisible(calleeInfoVisible));
return result;
}
@ -240,7 +240,7 @@ function _setJWT(store, next, action) {
}
}
return _maybeSetCallOverlayVisible(store, next, action);
return _maybeSetCalleeInfoVisible(store, next, action);
}
/**

View File

@ -2,25 +2,25 @@
import { equals, set, ReducerRegistry } from '../redux';
import { SET_CALL_OVERLAY_VISIBLE, SET_JWT } from './actionTypes';
import { SET_CALLEE_INFO_VISIBLE, SET_JWT } from './actionTypes';
/**
* The initial redux state of the feature jwt.
*
* @private
* @type {{
* callOverlayVisible: ?boolean
* calleeInfoVisible: ?boolean
* isGuest: boolean
* }}
*/
const _INITIAL_STATE = {
/**
* The indicator which determines whether (the) {@code CallOverlay} is
* The indicator which determines whether (the) {@code CalleeInfo} is
* visible.
*
* @type {boolean|undefined}
*/
callOverlayVisible: undefined,
calleeInfoVisible: undefined,
/**
* The indicator which determines whether the local participant is a guest
@ -44,8 +44,8 @@ ReducerRegistry.register(
'features/base/jwt',
(state = _INITIAL_STATE, action) => {
switch (action.type) {
case SET_CALL_OVERLAY_VISIBLE:
return set(state, 'callOverlayVisible', action.callOverlayVisible);
case SET_CALLEE_INFO_VISIBLE:
return set(state, 'calleeInfoVisible', action.calleeInfoVisible);
case SET_JWT: {
// eslint-disable-next-line no-unused-vars

View File

@ -9,12 +9,15 @@ import { connect as reactReduxConnect } from 'react-redux';
import { appNavigate } from '../../app';
import { connect, disconnect } from '../../base/connection';
import { DialogContainer } from '../../base/dialog';
import { CalleeInfo } from '../../base/jwt';
import { Container, LoadingIndicator } from '../../base/react';
import { createDesiredLocalTracks } from '../../base/tracks';
import { Filmstrip } from '../../filmstrip';
import { LargeVideo } from '../../large-video';
import { setToolboxVisible, Toolbox } from '../../toolbox';
import { abstractMapStateToProps } from '../functions';
import styles from './styles';
/**
@ -30,6 +33,14 @@ const _TOOLBOX_TIMEOUT_MS = 5000;
*/
type Props = {
/**
* The indication which determines if the {@code CalleeInfo} component
* should be shown or not.
*
* @private
*/
_calleeInfoVisible: boolean,
/**
* The indicator which determines that we are still connecting to the
* conference which includes establishing the XMPP connection and then
@ -189,6 +200,13 @@ class Conference extends Component<Props> {
*/}
<LargeVideo />
{/*
* If there is a ringing call, show the callee's info.
*/
this.props._calleeInfoVisible
&& <CalleeInfo />
}
{/*
* The activity/loading indicator goes above everything, except
* the toolbox/toolbars and the dialogs.
@ -375,6 +393,8 @@ function _mapStateToProps(state) {
= connecting || (connection && (joining || (!conference && !leaving)));
return {
...abstractMapStateToProps(state),
/**
* The indicator which determines that we are still connecting to the
* conference which includes establishing the XMPP connection and then

View File

@ -1,43 +1,48 @@
/* @flow */
// @flow
import _ from 'lodash';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect as reactReduxConnect } from 'react-redux';
import { connect, disconnect } from '../../base/connection';
import { DialogContainer } from '../../base/dialog';
import { CalleeInfo } from '../../base/jwt';
import { Filmstrip } from '../../filmstrip';
import { LargeVideo } from '../../large-video';
import { NotificationsContainer } from '../../notifications';
import { showToolbox, Toolbox } from '../../toolbox';
import { HideNotificationBarStyle } from '../../unsupported-browser';
declare var $: Function;
import { abstractMapStateToProps } from '../functions';
declare var APP: Object;
declare var interfaceConfig: Object;
/**
* The conference page of the Web application.
* The type of the React {@code Component} props of {@link Conference}.
*/
class Conference extends Component<*> {
_onShowToolbar: Function;
_originalOnShowToolbar: Function;
type Props = {
/**
* Conference component's property types.
*
* @static
* Whether or not the callee's info for a ringing call should be shown
* or not.
*/
static propTypes = {
/**
* Whether or not the current local user is recording the conference.
*
*/
_isRecording: PropTypes.bool,
_calleeInfoVisible: boolean,
dispatch: PropTypes.func
};
/**
* Whether or not the current local user is recording the conference.
*/
_isRecording: boolean,
dispatch: Function
}
/**
* The conference page of the Web application.
*/
class Conference extends Component<Props> {
_onShowToolbar: Function;
_originalOnShowToolbar: Function;
/**
* Initializes a new Conference instance.
@ -117,6 +122,10 @@ class Conference extends Component<*> {
<DialogContainer />
<NotificationsContainer />
{ this.props._calleeInfoVisible
&& <CalleeInfo />
}
{/*
* Temasys automatically injects a notification bar, if
* necessary, displayed at the top of the page notifying that
@ -152,6 +161,14 @@ class Conference extends Component<*> {
*/
function _mapStateToProps(state) {
return {
...abstractMapStateToProps(state),
/**
* Indicates if the current user is recording the conference, ie, they
* are a recorder.
*
* @private
*/
_isRecording: state['features/base/config'].iAmRecorder
};
}

View File

@ -0,0 +1,25 @@
// @flow
/**
* Maps parts of the redux state to {@link Toolbox} (React {@code Component})
* props.
*
* @param {Object} state - The redux state of which parts are to be mapped to
* {@code Conference} props.
* @protected
* @returns {{
* _calleeInfoVisible: boolean
* }}
*/
export function abstractMapStateToProps(state: Object): Object {
return {
/**
* The indication which determines if the {@code CalleeInfo} component
* should be shown or not.
*
* @private
* @type {boolean}
*/
_calleeInfoVisible: state['features/base/jwt'].calleeInfoVisible
};
}

View File

@ -1,7 +1,7 @@
/* @flow */
import { MiddlewareRegistry } from '../base/redux';
import { SET_CALL_OVERLAY_VISIBLE } from '../base/jwt';
import { SET_CALLEE_INFO_VISIBLE } from '../base/jwt';
import Filmstrip from '../../../modules/UI/videolayout/Filmstrip';
@ -10,13 +10,13 @@ declare var APP: Object;
// eslint-disable-next-line no-unused-vars
MiddlewareRegistry.register(({ getState }) => next => action => {
switch (action.type) {
case SET_CALL_OVERLAY_VISIBLE:
case SET_CALLEE_INFO_VISIBLE:
if (typeof APP !== 'undefined') {
const oldValue
= Boolean(getState()['features/base/jwt'].callOverlayVisible);
= Boolean(getState()['features/base/jwt'].calleeInfoVisible);
const result = next(action);
const newValue
= Boolean(getState()['features/base/jwt'].callOverlayVisible);
= Boolean(getState()['features/base/jwt'].calleeInfoVisible);
oldValue === newValue

View File

@ -185,7 +185,7 @@ function _mapStateToProps(state) {
const isAnyOverlayVisible = (connectionEstablished && haveToReload)
|| isMediaPermissionPromptVisible
|| suspendDetected
|| state['features/base/jwt'].callOverlayVisible;
|| state['features/base/jwt'].calleeInfoVisible;
const { enabled, notifications } = state['features/notifications'];

View File

@ -3,8 +3,6 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { CallOverlay } from '../../base/jwt';
import PageReloadFilmstripOnlyOverlay from './PageReloadFilmstripOnlyOverlay';
import PageReloadOverlay from './PageReloadOverlay';
import SuspendedFilmstripOnlyOverlay from './SuspendedFilmstripOnlyOverlay';
@ -95,8 +93,7 @@ function _getOverlays(filmstripOnly) {
overlays = _nonFilmstripOnlyOverlays = [
PageReloadOverlay,
SuspendedOverlay,
UserMediaPermissionsOverlay,
CallOverlay
UserMediaPermissionsOverlay
];
}

View File

@ -187,7 +187,7 @@ export function hideToolbox(force: boolean = false): Function {
if (!force
&& (hovered
|| state['features/base/jwt'].callOverlayVisible
|| state['features/base/jwt'].calleeInfoVisible
|| SideContainerToggler.isVisible())) {
dispatch(
setToolboxTimeout(