Browse Source

[React] Call/ring overlay

Rewrite the non-React RingOverlay into the React Component CallOverlay
in a way which makes it easier to port to React Native.
j8
Lyubo Marinov 8 years ago
parent
commit
d437f3db03

+ 2
- 3
conference.js View File

@@ -386,9 +386,8 @@ class ConferenceConnector {
386 386
     _onConferenceFailed(err, ...params) {
387 387
         APP.store.dispatch(conferenceFailed(room, err, ...params));
388 388
         logger.error('CONFERENCE FAILED:', err, ...params);
389
-        APP.UI.hideRingOverlay();
390
-        switch (err) {
391 389
 
390
+        switch (err) {
392 391
         case ConferenceErrors.CONNECTION_ERROR:
393 392
             {
394 393
                 let [msg] = params;
@@ -2027,7 +2026,7 @@ export default {
2027 2026
      */
2028 2027
     hangup(requestFeedback = false) {
2029 2028
         eventEmitter.emit(JitsiMeetConferenceEvents.BEFORE_HANGUP);
2030
-        APP.UI.hideRingOverlay();
2029
+
2031 2030
         let requestFeedbackPromise = requestFeedback
2032 2031
                 ? APP.UI.requestFeedbackOnHangup()
2033 2032
                 // false - because the thank you dialog shouldn't be displayed

+ 9
- 3
interface_config.js View File

@@ -70,7 +70,13 @@ var interfaceConfig = { // eslint-disable-line no-unused-vars
70 70
     ENABLE_FEEDBACK_ANIMATION: false,
71 71
     DISABLE_FOCUS_INDICATOR: false,
72 72
     DISABLE_DOMINANT_SPEAKER_INDICATOR: false,
73
-    // disables the ringing sound when the RingOverlay is shown.
73
+
74
+    /**
75
+     * Whether the ringing sound in the call/ring overlay is disabled. If
76
+     * {@code undefined}, defaults to {@code false}.
77
+     *
78
+     * @type {boolean}
79
+     */
74 80
     DISABLE_RINGING: false,
75 81
     AUDIO_LEVEL_PRIMARY_COLOR: "rgba(255,255,255,0.4)",
76 82
     AUDIO_LEVEL_SECONDARY_COLOR: "rgba(255,255,255,0.2)",
@@ -82,8 +88,8 @@ var interfaceConfig = { // eslint-disable-line no-unused-vars
82 88
 
83 89
     /**
84 90
      * Whether the mobile app Jitsi Meet is to be promoted to participants
85
-     * attempting to join a conference in a mobile Web browser. If undefined,
86
-     * default to true.
91
+     * attempting to join a conference in a mobile Web browser. If
92
+     * {@code undefined}, defaults to {@code true}.
87 93
      *
88 94
      * @type {boolean}
89 95
      */

+ 6
- 33
modules/UI/UI.js View File

@@ -19,18 +19,11 @@ import Filmstrip from "./videolayout/Filmstrip";
19 19
 import SettingsMenu from "./side_pannels/settings/SettingsMenu";
20 20
 import Profile from "./side_pannels/profile/Profile";
21 21
 import Settings from "./../settings/Settings";
22
-import RingOverlay from "./ring_overlay/RingOverlay";
23 22
 import UIErrors from './UIErrors';
24 23
 import { debounce } from "../util/helpers";
25 24
 
26
-
27
-import {
28
-    updateDeviceList
29
-} from '../../react/features/base/devices';
30
-import {
31
-    setAudioMuted,
32
-    setVideoMuted
33
-} from '../../react/features/base/media';
25
+import { updateDeviceList } from '../../react/features/base/devices';
26
+import { setAudioMuted, setVideoMuted } from '../../react/features/base/media';
34 27
 import {
35 28
     openDeviceSelectionDialog
36 29
 } from '../../react/features/device-selection';
@@ -368,10 +361,6 @@ UI.start = function () {
368 361
             "closeHtml": '<button type="button" tabIndex="-1">&times;</button>'
369 362
         };
370 363
     }
371
-
372
-    const { callee } = APP.store.getState()['features/jwt'];
373
-
374
-    callee && UI.showRingOverlay();
375 364
 };
376 365
 
377 366
 /**
@@ -492,7 +481,7 @@ UI.getSharedDocumentManager = () => etherpadManager;
492 481
 UI.addUser = function (user) {
493 482
     var id = user.getId();
494 483
     var displayName = user.getDisplayName();
495
-    UI.hideRingOverlay();
484
+
496 485
     if (UI.ContactList)
497 486
         UI.ContactList.addContact(id);
498 487
 
@@ -1371,17 +1360,6 @@ UI.setCameraButtonEnabled
1371 1360
 UI.setMicrophoneButtonEnabled
1372 1361
     = enabled => APP.store.dispatch(setAudioIconEnabled(enabled));
1373 1362
 
1374
-UI.showRingOverlay = function () {
1375
-    const { callee } = APP.store.getState()['features/jwt'];
1376
-
1377
-    callee && RingOverlay.show(callee, interfaceConfig.DISABLE_RINGING);
1378
-
1379
-    Filmstrip.toggleFilmstrip(false, false);
1380
-};
1381
-
1382
-UI.hideRingOverlay
1383
-    = () => RingOverlay.hide() && Filmstrip.toggleFilmstrip(true, false);
1384
-
1385 1363
 /**
1386 1364
  * Indicates if any the "top" overlays are currently visible. The check includes
1387 1365
  * the call/ring overlay, the suspended overlay, the GUM permissions overlay,
@@ -1390,16 +1368,11 @@ UI.hideRingOverlay
1390 1368
  * @returns {*|boolean} {true} if an overlay is visible; {false}, otherwise
1391 1369
  */
1392 1370
 UI.isOverlayVisible = function () {
1393
-    return this.isRingOverlayVisible() || this.overlayVisible;
1371
+    return (
1372
+        this.overlayVisible
1373
+            || APP.store.getState()['features/jwt'].callOverlayVisible);
1394 1374
 };
1395 1375
 
1396
-/**
1397
- * Indicates if the ring overlay is currently visible.
1398
- *
1399
- * @returns {*|boolean} {true} if the ring overlay is visible, {false} otherwise
1400
- */
1401
-UI.isRingOverlayVisible = () => RingOverlay.isVisible();
1402
-
1403 1376
 /**
1404 1377
  * Handles user's features changes.
1405 1378
  */

+ 0
- 178
modules/UI/ring_overlay/RingOverlay.js View File

@@ -1,178 +0,0 @@
1
-/* global $, APP */
2
-
3
-import UIEvents from "../../../service/UI/UIEvents";
4
-
5
-/**
6
- * Store the current ring overlay instance.
7
- * Note: We want to have only 1 instance at a time.
8
- */
9
-let overlay = null;
10
-
11
-/**
12
- * Handler for UIEvents.LARGE_VIDEO_AVATAR_VISIBLE event.
13
- * @param {boolean} shown indicates whether the avatar on the large video is
14
- *  currently displayed or not.
15
- */
16
-function onAvatarVisible(shown) {
17
-    overlay._changeBackground(shown);
18
-}
19
-
20
-/**
21
- * Shows ring overlay
22
- */
23
-class RingOverlay {
24
-    /**
25
-     *
26
-     * @param callee The callee (Object) as defined by the JWT support.
27
-     * @param {boolean} disableRinging if true the ringing sound wont be played.
28
-     */
29
-    constructor(callee, disableRinging) {
30
-        this._containerId = 'ringOverlay';
31
-        this._audioContainerId = 'ringOverlayRinging';
32
-        this.isRinging = true;
33
-        this.callee = callee;
34
-        this.disableRinging = disableRinging;
35
-        this.render();
36
-        if (!disableRinging)
37
-            this._initAudio();
38
-        this._timeout
39
-            = setTimeout(
40
-                    () => {
41
-                        this.destroy();
42
-                        this.render();
43
-                    },
44
-                    30000);
45
-    }
46
-
47
-    /**
48
-     * Initializes the audio element and setups the interval for playing it.
49
-     */
50
-    _initAudio() {
51
-        this.audio = document.getElementById(this._audioContainerId);
52
-        this.audio.play();
53
-        this.interval = setInterval(() => this.audio.play(), 5000);
54
-    }
55
-
56
-    /**
57
-     * Chagnes the background of the ring overlay.
58
-     * @param {boolean} solid - if true the new background will be the solid
59
-     * one, otherwise the background will be default one.
60
-     * NOTE: The method just toggles solidBG css class.
61
-     */
62
-    _changeBackground(solid) {
63
-        const container = $("#" + this._containerId);
64
-
65
-        if (solid) {
66
-            container.addClass("solidBG");
67
-        } else {
68
-            container.removeClass("solidBG");
69
-        }
70
-    }
71
-
72
-    /**
73
-     * Builds and appends the ring overlay to the html document
74
-     */
75
-    _getHtmlStr(callee) {
76
-        let callingLabel = this.isRinging ? "<p>Calling...</p>" : "";
77
-        let callerStateLabel =  this.isRinging ? "" : " isn't available";
78
-        let audioHTML = this.disableRinging ? ""
79
-            : "<audio id=\"" + this._audioContainerId
80
-                + "\" src=\"./sounds/ring.ogg\" />";
81
-
82
-        return `
83
-            <div id="${this._containerId}" class='ringing' >
84
-                <div class='ringing__content'>
85
-                    ${callingLabel}
86
-                    <img class='ringing__avatar' src="${callee.avatarUrl}" />
87
-                    <div class="ringing__caller-info">
88
-                        <p>${callee.name}${callerStateLabel}</p>
89
-                    </div>
90
-                </div>
91
-                ${audioHTML}
92
-            </div>`;
93
-    }
94
-
95
-    /**
96
-     *
97
-     */
98
-    render() {
99
-        this.htmlStr = this._getHtmlStr(this.callee);
100
-        this._attach();
101
-    }
102
-
103
-    /**
104
-     * Destroys and clears all the objects (html elements and audio interval)
105
-     * related to the ring overlay.
106
-     */
107
-    destroy() {
108
-        this.isRinging = false;
109
-        this._stopAudio();
110
-        this._detach();
111
-    }
112
-
113
-    _attach() {
114
-        $("body").append(this.htmlStr);
115
-    }
116
-
117
-    _detach() {
118
-        $(`#${this._containerId}`).remove();
119
-    }
120
-
121
-    /**
122
-     * Stops the ringing and clears related timers.
123
-     */
124
-    _stopAudio() {
125
-        if (this.interval) {
126
-            clearInterval(this.interval);
127
-        }
128
-        if (this._timeout) {
129
-            clearTimeout(this._timeout);
130
-        }
131
-    }
132
-}
133
-
134
-export default {
135
-    /**
136
-     * Shows the ring overlay for the passed callee.
137
-     *
138
-     * @param {Object} callee - The callee. Object containing data about
139
-     * callee.
140
-     * @param {boolean} disableRinging - If true the ringing sound won't be
141
-     * played.
142
-     * @returns {void}
143
-     */
144
-    show(callee, disableRinging = false) {
145
-        if (overlay) {
146
-            this.hide();
147
-        }
148
-
149
-        overlay = new RingOverlay(callee, disableRinging);
150
-        APP.UI.addListener(UIEvents.LARGE_VIDEO_AVATAR_VISIBLE,
151
-            onAvatarVisible);
152
-    },
153
-
154
-    /**
155
-     * Hides the ring overlay. Destroys all the elements related to the ring
156
-     * overlay.
157
-     */
158
-    hide() {
159
-        if (!overlay) {
160
-            return false;
161
-        }
162
-        overlay.destroy();
163
-        overlay = null;
164
-        APP.UI.removeListener(UIEvents.LARGE_VIDEO_AVATAR_VISIBLE,
165
-            onAvatarVisible);
166
-        return true;
167
-    },
168
-
169
-    /**
170
-     * Checks whether or not the ring overlay is currently displayed.
171
-     *
172
-     * @returns {boolean} true if the ring overlay is currently displayed or
173
-     * false otherwise.
174
-     */
175
-    isVisible() {
176
-        return overlay !== null;
177
-    }
178
-};

+ 0
- 1
react/features/base/connection/actions.web.js View File

@@ -77,7 +77,6 @@ export function connect() {
77 77
             }
78 78
         })
79 79
             .catch(error => {
80
-                APP.UI.hideRingOverlay();
81 80
                 APP.API.notifyConferenceLeft(APP.conference.roomName);
82 81
                 logger.error(error);
83 82
 

+ 27
- 9
react/features/filmstrip/middleware.js View File

@@ -1,6 +1,10 @@
1
-import UIEvents from '../../../service/UI/UIEvents';
1
+/* @flow */
2 2
 
3 3
 import { MiddlewareRegistry } from '../base/redux';
4
+import { SET_CALL_OVERLAY_VISIBLE } from '../jwt';
5
+
6
+import Filmstrip from '../../../modules/UI/videolayout/Filmstrip';
7
+import UIEvents from '../../../service/UI/UIEvents';
4 8
 
5 9
 import {
6 10
     SET_FILMSTRIP_REMOTE_VIDEOS_VISIBLITY,
@@ -10,19 +14,33 @@ import {
10 14
 declare var APP: Object;
11 15
 
12 16
 // eslint-disable-next-line no-unused-vars
13
-MiddlewareRegistry.register(store => next => action => {
14
-    const result = next(action);
15
-
17
+MiddlewareRegistry.register(({ getState }) => next => action => {
16 18
     switch (action.type) {
17
-    case SET_FILMSTRIP_REMOTE_VIDEOS_VISIBLITY:
18
-    case SET_FILMSTRIP_VISIBILITY: {
19
-        if (typeof APP !== 'undefined') {
20
-            APP.UI.emitEvent(UIEvents.UPDATED_FILMSTRIP_DISPLAY);
19
+    case SET_CALL_OVERLAY_VISIBLE:
20
+        if (typeof APP === 'undefined') {
21
+            const oldValue
22
+                = Boolean(getState()['features/jwt'].callOverlayVisible);
23
+            const result = next(action);
24
+            const newValue
25
+                = Boolean(getState()['features/jwt'].callOverlayVisible);
21 26
 
27
+            oldValue === newValue
28
+                || Filmstrip.toggleFilmstrip(!newValue, false);
29
+
30
+            return result;
22 31
         }
23 32
         break;
33
+
34
+    case SET_FILMSTRIP_REMOTE_VIDEOS_VISIBLITY:
35
+    case SET_FILMSTRIP_VISIBILITY: {
36
+        const result = next(action);
37
+
38
+        typeof APP === 'undefined'
39
+            || APP.UI.emitEvent(UIEvents.UPDATED_FILMSTRIP_DISPLAY);
40
+
41
+        return result;
24 42
     }
25 43
     }
26 44
 
27
-    return result;
45
+    return next(action);
28 46
 });

+ 10
- 0
react/features/jwt/actionTypes.js View File

@@ -1,3 +1,13 @@
1
+/**
2
+ * The type of redux action which sets the visibility of {@code CallOverlay}.
3
+ *
4
+ * {
5
+ *     type: SET_CALL_OVERLAY_VISIBLE,
6
+ *     callOverlayVisible: boolean
7
+ * }
8
+ */
9
+export const SET_CALL_OVERLAY_VISIBLE = Symbol('SET_CALL_OVERLAY_VISIBLE');
10
+
1 11
 /**
2 12
  * The type of redux action which stores a specific JSON Web Token (JWT) into
3 13
  * the redux store.

+ 22
- 1
react/features/jwt/actions.js View File

@@ -1,6 +1,27 @@
1 1
 /* @flow */
2 2
 
3
-import { SET_JWT } from './actionTypes';
3
+import { SET_CALL_OVERLAY_VISIBLE, SET_JWT } from './actionTypes';
4
+
5
+/**
6
+ * Sets the visibility of {@code CallOverlay}.
7
+ *
8
+ * @param {boolean|undefined} callOverlayVisible - If {@code CallOverlay} is to
9
+ * be displayed/visible, then {@code true}; otherwise, {@code false} or
10
+ * {@code undefined}.
11
+ * @returns {{
12
+ *     type: SET_CALL_OVERLAY_VISIBLE,
13
+ *     callOverlayVisible: (boolean|undefined)
14
+ * }}
15
+ */
16
+export function setCallOverlayVisible(callOverlayVisible: boolean) {
17
+    return (dispatch: Dispatch<*>, getState: Function) => {
18
+        getState()['features/jwt'].callOverlayVisible === callOverlayVisible
19
+            || dispatch({
20
+                type: SET_CALL_OVERLAY_VISIBLE,
21
+                callOverlayVisible
22
+            });
23
+    };
24
+}
4 25
 
5 26
 /**
6 27
  * Stores a specific JSON Web Token (JWT) into the redux store.

+ 288
- 0
react/features/jwt/components/CallOverlay.js View File

@@ -0,0 +1,288 @@
1
+/* @flow */
2
+
3
+import React, { Component } from 'react';
4
+import { connect } from 'react-redux';
5
+
6
+import { Audio } from '../../base/media';
7
+import { Avatar } from '../../base/participants';
8
+import { Container, Text } from '../../base/react';
9
+import UIEvents from '../../../../service/UI/UIEvents';
10
+
11
+declare var $: Object;
12
+declare var APP: Object;
13
+declare var interfaceConfig: Object;
14
+
15
+/**
16
+ * Implements a React {@link Component} which depicts the establishment of a
17
+ * call with a specific remote callee.
18
+ *
19
+ * @extends Component
20
+ */
21
+class CallOverlay extends Component {
22
+    /**
23
+     * The (reference to the) {@link Audio} which plays/renders the audio
24
+     * depicting the ringing phase of the call establishment represented by this
25
+     * {@code CallOverlay}.
26
+     */
27
+    _audio: ?Audio
28
+
29
+    _onLargeVideoAvatarVisible: Function
30
+
31
+    _playAudioInterval: ?number
32
+
33
+    _ringingTimeout: ?number
34
+
35
+    _setAudio: Function
36
+
37
+    state: {
38
+
39
+        /**
40
+         * The CSS class (name), if any, to add to this {@code CallOverlay}.
41
+         *
42
+         * @type {string}
43
+         */
44
+        className: ?string,
45
+
46
+        /**
47
+         * The indicator which determines whether this {@code CallOverlay}
48
+         * should play/render audio to indicate the ringing phase of the
49
+         * call establishment between the local participant and the
50
+         * associated remote callee.
51
+         *
52
+         * @type {boolean}
53
+         */
54
+        renderAudio: boolean,
55
+
56
+        /**
57
+         * The indicator which determines whether this {@code CallOverlay}
58
+         * is depicting the ringing phase of the call establishment between
59
+         * the local participant and the associated remote callee or the
60
+         * phase afterwards when the callee has not answered the call for a
61
+         * period of time and, consequently, is considered unavailable.
62
+         *
63
+         * @type {boolean}
64
+         */
65
+        ringing: boolean
66
+    }
67
+
68
+    /**
69
+     * {@code CallOverlay} component's property types.
70
+     *
71
+     * @static
72
+     */
73
+    static propTypes = {
74
+        _callee: React.PropTypes.object
75
+    };
76
+
77
+    /**
78
+     * Initializes a new {@code CallOverlay} instance.
79
+     *
80
+     * @param {Object} props - The read-only React {@link Component} props with
81
+     * which the new instance is to be initialized.
82
+     */
83
+    constructor(props) {
84
+        super(props);
85
+
86
+        this.state = {
87
+            className: undefined,
88
+            renderAudio:
89
+                typeof interfaceConfig !== 'object'
90
+                    || !interfaceConfig.DISABLE_RINGING,
91
+            ringing: true
92
+        };
93
+
94
+        this._onLargeVideoAvatarVisible
95
+            = this._onLargeVideoAvatarVisible.bind(this);
96
+        this._setAudio = this._setAudio.bind(this);
97
+
98
+        if (typeof APP === 'object') {
99
+            APP.UI.addListener(
100
+                UIEvents.LARGE_VIDEO_AVATAR_VISIBLE,
101
+                this._onLargeVideoAvatarVisible);
102
+        }
103
+    }
104
+
105
+    /**
106
+     * Sets up timeouts such as the timeout to end the ringing phase of the call
107
+     * establishment depicted by this {@code CallOverlay}.
108
+     *
109
+     * @inheritdoc
110
+     */
111
+    componentDidMount() {
112
+        // Set up the timeout to end the ringing phase of the call establishment
113
+        // depicted by this CallOverlay.
114
+        if (this.state.ringing && !this._ringingTimeout) {
115
+            this._ringingTimeout
116
+                = setTimeout(
117
+                    () => {
118
+                        this._pauseAudio();
119
+
120
+                        this._ringingTimeout = undefined;
121
+                        this.setState({
122
+                            ringing: false
123
+                        });
124
+                    },
125
+                    30000);
126
+        }
127
+
128
+        this._playAudio();
129
+    }
130
+
131
+    /**
132
+     * Cleans up before this {@code Calleverlay} is unmounted and destroyed.
133
+     *
134
+     * @inheritdoc
135
+     */
136
+    componentWillUnmount() {
137
+        this._pauseAudio();
138
+
139
+        if (this._ringingTimeout) {
140
+            clearTimeout(this._ringingTimeout);
141
+            this._ringingTimeout = undefined;
142
+        }
143
+
144
+        if (typeof APP === 'object') {
145
+            APP.UI.removeListener(
146
+                UIEvents.LARGE_VIDEO_AVATAR_VISIBLE,
147
+                this._onLargeVideoAvatarVisible);
148
+        }
149
+    }
150
+
151
+    /**
152
+     * Implements React's {@link Component#render()}.
153
+     *
154
+     * @inheritdoc
155
+     * @returns {ReactElement}
156
+     */
157
+    render() {
158
+        const { className, ringing } = this.state;
159
+        const { avatarUrl, name } = this.props._callee;
160
+
161
+        return (
162
+            <Container
163
+                className = { `ringing ${className || ''}` }
164
+                id = 'ringOverlay'>
165
+                <Container className = 'ringing__content'>
166
+                    { ringing ? <Text>Calling...</Text> : null }
167
+                    <Avatar
168
+                        className = 'ringing__avatar'
169
+                        uri = { avatarUrl } />
170
+                    <Container className = 'ringing__caller-info'>
171
+                        <Text>
172
+                            { name }
173
+                            { ringing ? null : ' isn\'t available' }
174
+                        </Text>
175
+                    </Container>
176
+                </Container>
177
+                { this._renderAudio() }
178
+            </Container>
179
+        );
180
+    }
181
+
182
+    /**
183
+     * Notifies this {@code CallOverlay} that the visibility of the
184
+     * participant's avatar in the large video has changed.
185
+     *
186
+     * @param {boolean} largeVideoAvatarVisible - If the avatar in the large
187
+     * video (i.e. of the participant on the stage) is visible, then
188
+     * {@code true}; otherwise, {@code false}.
189
+     * @private
190
+     * @returns {void}
191
+     */
192
+    _onLargeVideoAvatarVisible(largeVideoAvatarVisible: boolean) {
193
+        this.setState({
194
+            className: largeVideoAvatarVisible ? 'solidBG' : undefined
195
+        });
196
+    }
197
+
198
+    /**
199
+     * Stops the playback of the audio which represents the ringing phase of the
200
+     * call establishment depicted by this {@code CallOverlay}.
201
+     *
202
+     * @private
203
+     * @returns {void}
204
+     */
205
+    _pauseAudio() {
206
+        const audio = this._audio;
207
+
208
+        if (audio) {
209
+            audio.pause();
210
+        }
211
+        if (this._playAudioInterval) {
212
+            clearInterval(this._playAudioInterval);
213
+            this._playAudioInterval = undefined;
214
+        }
215
+    }
216
+
217
+    /**
218
+     * Starts the playback of the audio which represents the ringing phase of
219
+     * the call establishment depicted by this {@code CallOverlay}.
220
+     *
221
+     * @private
222
+     * @returns {void}
223
+     */
224
+    _playAudio() {
225
+        if (this._audio) {
226
+            this._audio.play();
227
+            if (!this._playAudioInterval) {
228
+                this._playAudioInterval
229
+                    = setInterval(() => this._playAudio(), 5000);
230
+            }
231
+        }
232
+    }
233
+
234
+    /**
235
+     * Renders an audio element to represent the ringing phase of the call
236
+     * establishment represented by this {@code CallOverlay}.
237
+     *
238
+     * @private
239
+     * @returns {ReactElement}
240
+     */
241
+    _renderAudio() {
242
+        if (this.state.renderAudio && this.state.ringing) {
243
+            return (
244
+                <Audio
245
+                    ref = { this._setAudio }
246
+                    src = './sounds/ring.ogg' />
247
+            );
248
+        }
249
+
250
+        return null;
251
+    }
252
+
253
+    /**
254
+     * Sets the (reference to the) {@link Audio} which renders the ringing phase
255
+     * of the call establishment represented by this {@code CallOverlay}.
256
+     *
257
+     * @param {Audio} audio - The (reference to the) {@code Audio} which
258
+     * plays/renders the audio depicting the ringing phase of the call
259
+     * establishment represented by this {@code CallOverlay}.
260
+     * @private
261
+     * @returns {void}
262
+     */
263
+    _setAudio(audio) {
264
+        this._audio = audio;
265
+    }
266
+}
267
+
268
+/**
269
+ * Maps (parts of) the redux state to {@code CallOverlay}'s props.
270
+ *
271
+ * @param {Object} state - The redux state.
272
+ * @private
273
+ * @returns {{
274
+ *     _callee: Object
275
+ * }}
276
+ */
277
+function _mapStateToProps(state) {
278
+    return {
279
+        /**
280
+         *
281
+         * @private
282
+         * @type {Object}
283
+         */
284
+        _callee: state['features/jwt'].callee
285
+    };
286
+}
287
+
288
+export default connect(_mapStateToProps)(CallOverlay);

+ 1
- 0
react/features/jwt/components/index.js View File

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

+ 1
- 0
react/features/jwt/index.js View File

@@ -1,4 +1,5 @@
1 1
 export * from './actions';
2
+export * from './components';
2 3
 export * from './functions';
3 4
 
4 5
 import './middleware';

+ 100
- 13
react/features/jwt/middleware.js View File

@@ -1,22 +1,38 @@
1 1
 import jwtDecode from 'jwt-decode';
2 2
 
3
+import {
4
+    CONFERENCE_FAILED,
5
+    CONFERENCE_LEFT,
6
+    CONFERENCE_WILL_LEAVE,
7
+    SET_ROOM
8
+} from '../base/conference';
3 9
 import { SET_CONFIG } from '../base/config';
4 10
 import { SET_LOCATION_URL } from '../base/connection';
11
+import { LIB_INIT_ERROR } from '../base/lib-jitsi-meet';
12
+import { PARTICIPANT_JOINED } from '../base/participants';
5 13
 import { MiddlewareRegistry } from '../base/redux';
6 14
 
7
-import { setJWT } from './actions';
15
+import { setCallOverlayVisible, setJWT } from './actions';
8 16
 import { SET_JWT } from './actionTypes';
9 17
 import { parseJWTFromURLParams } from './functions';
10 18
 
11 19
 /**
12 20
  * Middleware to parse token data upon setting a new room URL.
13 21
  *
14
- * @param {Store} store - The Redux store.
22
+ * @param {Store} store - The redux store.
15 23
  * @private
16 24
  * @returns {Function}
17 25
  */
18 26
 MiddlewareRegistry.register(store => next => action => {
19 27
     switch (action.type) {
28
+    case CONFERENCE_FAILED:
29
+    case CONFERENCE_LEFT:
30
+    case CONFERENCE_WILL_LEAVE:
31
+    case LIB_INIT_ERROR:
32
+    case PARTICIPANT_JOINED:
33
+    case SET_ROOM:
34
+        return _maybeSetCallOverlayVisible(store, next, action);
35
+
20 36
     case SET_CONFIG:
21 37
     case SET_LOCATION_URL:
22 38
         // XXX The JSON Web Token (JWT) is not the only piece of state that we
@@ -33,16 +49,87 @@ MiddlewareRegistry.register(store => next => action => {
33 49
     return next(action);
34 50
 });
35 51
 
52
+/**
53
+ * Notifies the feature jwt that a specific {@code action} is being dispatched
54
+ * within a specific redux {@code store} which may have an effect on the
55
+ * visiblity of (the) {@code CallOverlay}.
56
+ *
57
+ * @param {Store} store - The redux store in which the specified {@code action}
58
+ * is being dispatched.
59
+ * @param {Dispatch} next - The redux dispatch function to dispatch the
60
+ * specified {@code action} to the specified {@code store}.
61
+ * @param {Action} action - The redux action which is being dispatched in the
62
+ * specified {@code store}.
63
+ * @private
64
+ * @returns {Object} The new state that is the result of the reduction of the
65
+ * specified {@code action}.
66
+ */
67
+function _maybeSetCallOverlayVisible({ dispatch, getState }, next, action) {
68
+    const result = next(action);
69
+
70
+    const state = getState();
71
+    const stateFeaturesJWT = state['features/jwt'];
72
+    let callOverlayVisible;
73
+
74
+    if (stateFeaturesJWT.callee) {
75
+        const { conference, leaving, room } = state['features/base/conference'];
76
+
77
+        // XXX The CallOverlay is to be displayed/visible as soon as
78
+        // possible including even before the conference is joined.
79
+        if (room && (!conference || conference !== leaving)) {
80
+            switch (action.type) {
81
+            case CONFERENCE_FAILED:
82
+            case CONFERENCE_LEFT:
83
+            case CONFERENCE_WILL_LEAVE:
84
+            case LIB_INIT_ERROR:
85
+                // Because the CallOverlay is to be displayed/visible as soon as
86
+                // possible even before the connection is established and/or the
87
+                // conference is joined, it is very complicated to figure out
88
+                // based on the current state alone. In order to reduce the
89
+                // risks of displaying the CallOverly at inappropirate times, do
90
+                // not even attempt to figure out based on the current state.
91
+                // The (redux) actions listed above are also the equivalents of
92
+                // the execution ponints at which APP.UI.hideRingOverlay() used
93
+                // to be invoked.
94
+                break;
95
+
96
+            default: {
97
+                // The CallOverlay it to no longer be displayed/visible as soon
98
+                // as another participant joins.
99
+                const participants = state['features/base/participants'];
100
+
101
+                callOverlayVisible
102
+                    = Boolean(
103
+                            participants
104
+                                && participants.length === 1
105
+                                && participants[0].local);
106
+
107
+                // However, the CallDialog is not to be displayed/visible again
108
+                // after all remote participants leave.
109
+                if (callOverlayVisible
110
+                        && stateFeaturesJWT.callOverlayVisible === false) {
111
+                    callOverlayVisible = false;
112
+                }
113
+                break;
114
+            }
115
+            }
116
+        }
117
+    }
118
+    dispatch(setCallOverlayVisible(callOverlayVisible));
119
+
120
+    return result;
121
+}
122
+
36 123
 /**
37 124
  * Notifies the feature jwt that the action {@link SET_CONFIG} or
38
- * {@link SET_LOCATION_URL} is being dispatched within a specific Redux
125
+ * {@link SET_LOCATION_URL} is being dispatched within a specific redux
39 126
  * {@code store}.
40 127
  *
41
- * @param {Store} store - The Redux store in which the specified {@code action}
128
+ * @param {Store} store - The redux store in which the specified {@code action}
42 129
  * is being dispatched.
43
- * @param {Dispatch} next - The Redux dispatch function to dispatch the
130
+ * @param {Dispatch} next - The redux dispatch function to dispatch the
44 131
  * specified {@code action} to the specified {@code store}.
45
- * @param {Action} action - The Redux action {@code SET_CONFIG} or
132
+ * @param {Action} action - The redux action {@code SET_CONFIG} or
46 133
  * {@code SET_LOCATION_URL} which is being dispatched in the specified
47 134
  * {@code store}.
48 135
  * @private
@@ -65,26 +152,26 @@ function _setConfigOrLocationURL({ dispatch, getState }, next, action) {
65 152
 
66 153
 /**
67 154
  * Notifies the feature jwt that the action {@link SET_JWT} is being dispatched
68
- * within a specific Redux {@code store}.
155
+ * within a specific redux {@code store}.
69 156
  *
70
- * @param {Store} store - The Redux store in which the specified {@code action}
157
+ * @param {Store} store - The redux store in which the specified {@code action}
71 158
  * is being dispatched.
72
- * @param {Dispatch} next - The Redux dispatch function to dispatch the
159
+ * @param {Dispatch} next - The redux dispatch function to dispatch the
73 160
  * specified {@code action} to the specified {@code store}.
74
- * @param {Action} action - The Redux action {@code SET_JWT} which is being
161
+ * @param {Action} action - The redux action {@code SET_JWT} which is being
75 162
  * dispatched in the specified {@code store}.
76 163
  * @private
77 164
  * @returns {Object} The new state that is the result of the reduction of the
78 165
  * specified {@code action}.
79 166
  */
80
-function _setJWT({ getState }, next, action) {
167
+function _setJWT(store, next, action) {
81 168
     // eslint-disable-next-line no-unused-vars
82 169
     const { jwt, type, ...actionPayload } = action;
83 170
 
84 171
     if (jwt && !Object.keys(actionPayload).length) {
85 172
         const {
86 173
             enableUserRolesBasedOnToken
87
-        } = getState()['features/base/config'];
174
+        } = store.getState()['features/base/config'];
88 175
 
89 176
         action.isGuest = !enableUserRolesBasedOnToken;
90 177
 
@@ -103,5 +190,5 @@ function _setJWT({ getState }, next, action) {
103 190
         }
104 191
     }
105 192
 
106
-    return next(action);
193
+    return _maybeSetCallOverlayVisible(store, next, action);
107 194
 }

+ 13
- 2
react/features/jwt/reducer.js View File

@@ -1,6 +1,6 @@
1
-import { equals, ReducerRegistry } from '../base/redux';
1
+import { equals, set, ReducerRegistry } from '../base/redux';
2 2
 
3
-import { SET_JWT } from './actionTypes';
3
+import { SET_CALL_OVERLAY_VISIBLE, SET_JWT } from './actionTypes';
4 4
 
5 5
 /**
6 6
  * The initial redux state of the feature jwt.
@@ -11,6 +11,14 @@ import { SET_JWT } from './actionTypes';
11 11
  * }}
12 12
  */
13 13
 const _INITIAL_STATE = {
14
+    /**
15
+     * The indicator which determines whether (the) {@code CallOverlay} is
16
+     * visible.
17
+     *
18
+     * @type {boolean|undefined}
19
+     */
20
+    callOverlayVisible: undefined,
21
+
14 22
     /**
15 23
      * The indicator which determines whether the local participant is a guest
16 24
      * in the conference.
@@ -31,6 +39,9 @@ const _INITIAL_STATE = {
31 39
  */
32 40
 ReducerRegistry.register('features/jwt', (state = _INITIAL_STATE, action) => {
33 41
     switch (action.type) {
42
+    case SET_CALL_OVERLAY_VISIBLE:
43
+        return set(state, 'callOverlayVisible', action.callOverlayVisible);
44
+
34 45
     case SET_JWT: {
35 46
         // eslint-disable-next-line no-unused-vars
36 47
         const { type, ...payload } = action;

+ 37
- 14
react/features/overlay/components/OverlayContainer.js View File

@@ -1,6 +1,8 @@
1 1
 import React, { Component } from 'react';
2 2
 import { connect } from 'react-redux';
3 3
 
4
+import { CallOverlay } from '../../jwt';
5
+
4 6
 import PageReloadFilmstripOnlyOverlay from './PageReloadFilmstripOnlyOverlay';
5 7
 import PageReloadOverlay from './PageReloadOverlay';
6 8
 import SuspendedFilmstripOnlyOverlay from './SuspendedFilmstripOnlyOverlay';
@@ -33,6 +35,15 @@ class OverlayContainer extends Component {
33 35
          */
34 36
         _browser: React.PropTypes.string,
35 37
 
38
+        /**
39
+         * The indicator which determines whether the {@link CallOverlay} is
40
+         * displayed/visible.
41
+         *
42
+         * @private
43
+         * @type {boolean}
44
+         */
45
+        _callOverlayVisible: React.PropTypes.bool,
46
+
36 47
         /**
37 48
          * The indicator which determines whether the status of the
38 49
          * JitsiConnection object has been "established" or not.
@@ -172,6 +183,8 @@ class OverlayContainer extends Component {
172 183
             props = {
173 184
                 browser: this.props._browser
174 185
             };
186
+        } else if (this.props._callOverlayVisible) {
187
+            overlayComponent = CallOverlay;
175 188
         }
176 189
 
177 190
         return (
@@ -182,17 +195,18 @@ class OverlayContainer extends Component {
182 195
 }
183 196
 
184 197
 /**
185
- * Maps (parts of) the Redux state to the associated OverlayContainer's props.
198
+ * Maps (parts of) the redux state to the associated OverlayContainer's props.
186 199
  *
187
- * @param {Object} state - The Redux state.
200
+ * @param {Object} state - The redux state.
188 201
  * @returns {{
189 202
  *     _browser: string,
190
- *     _connectionEstablished: bool,
191
- *     _haveToReload: bool,
192
- *     _isNetworkFailure: bool,
193
- *     _isMediaPermissionPromptVisible: bool,
203
+ *     _callOverlayVisible: boolean,
204
+ *     _connectionEstablished: boolean,
205
+ *     _haveToReload: boolean,
206
+ *     _isNetworkFailure: boolean,
207
+ *     _isMediaPermissionPromptVisible: boolean,
194 208
  *     _reason: string,
195
- *     _suspendDetected: bool
209
+ *     _suspendDetected: boolean
196 210
  * }}
197 211
  * @private
198 212
  */
@@ -203,18 +217,27 @@ function _mapStateToProps(state) {
203 217
         /**
204 218
          * The browser which is used currently.
205 219
          *
206
-         * NOTE: Used by UserMediaPermissionsOverlay only.
220
+         * NOTE: Used by {@link UserMediaPermissionsOverlay} only.
207 221
          *
208 222
          * @private
209 223
          * @type {string}
210 224
          */
211 225
         _browser: stateFeaturesOverlay.browser,
212 226
 
227
+        /**
228
+         * The indicator which determines whether the {@link CallOverlay} is
229
+         * displayed/visible.
230
+         *
231
+         * @private
232
+         * @type {boolean}
233
+         */
234
+        _callOverlayVisible: Boolean(state['features/jwt'].callOverlayVisible),
235
+
213 236
         /**
214 237
          * The indicator which determines whether the status of the
215 238
          * JitsiConnection object has been "established" or not.
216 239
          *
217
-         * NOTE: Used by PageReloadOverlay only.
240
+         * NOTE: Used by {@link PageReloadOverlay} only.
218 241
          *
219 242
          * @private
220 243
          * @type {boolean}
@@ -225,7 +248,7 @@ function _mapStateToProps(state) {
225 248
          * The indicator which determines whether a critical error for reload
226 249
          * has been received.
227 250
          *
228
-         * NOTE: Used by PageReloadOverlay only.
251
+         * NOTE: Used by {@link PageReloadOverlay} only.
229 252
          *
230 253
          * @private
231 254
          * @type {boolean}
@@ -236,7 +259,7 @@ function _mapStateToProps(state) {
236 259
          * The indicator which determines whether the GUM permissions prompt is
237 260
          * displayed or not.
238 261
          *
239
-         * NOTE: Used by UserMediaPermissionsOverlay only.
262
+         * NOTE: Used by {@link UserMediaPermissionsOverlay} only.
240 263
          *
241 264
          * @private
242 265
          * @type {boolean}
@@ -248,7 +271,7 @@ function _mapStateToProps(state) {
248 271
          * The indicator which determines whether the reload was caused by
249 272
          * network failure.
250 273
          *
251
-         * NOTE: Used by PageReloadOverlay only.
274
+         * NOTE: Used by {@link PageReloadOverlay} only.
252 275
          *
253 276
          * @private
254 277
          * @type {boolean}
@@ -258,7 +281,7 @@ function _mapStateToProps(state) {
258 281
         /**
259 282
          * The reason for the error that will cause the reload.
260 283
          *
261
-         * NOTE: Used by PageReloadOverlay only.
284
+         * NOTE: Used by {@link PageReloadOverlay} only.
262 285
          *
263 286
          * @private
264 287
          * @type {string}
@@ -269,7 +292,7 @@ function _mapStateToProps(state) {
269 292
          * The indicator which determines whether the GUM permissions prompt is
270 293
          * displayed or not.
271 294
          *
272
-         * NOTE: Used by SuspendedOverlay only.
295
+         * NOTE: Used by {@link SuspendedOverlay} only.
273 296
          *
274 297
          * @private
275 298
          * @type {string}

+ 1
- 1
react/features/toolbox/actions.web.js View File

@@ -178,7 +178,7 @@ export function hideToolbox(force: boolean = false): Function {
178 178
 
179 179
         if (!force
180 180
                 && (hovered
181
-                    || APP.UI.isRingOverlayVisible()
181
+                    || state['features/jwt'].callOverlayVisible
182 182
                     || SideContainerToggler.isVisible())) {
183 183
             dispatch(
184 184
                 setToolboxTimeout(

Loading…
Cancel
Save