Переглянути джерело

ref(hangup): clean up some UI state on hangup

- Reset some state on the singletons conference
  and VideoLayout.
- Add a way for LocalVideo to clean itself up
  by sharing logic with the other SmallVideos.
- Add clearing of chat messages so they don't
  linger.
- Remove some UI event listeners.
j8
Leonard Kim 6 роки тому
джерело
коміт
14cc4ea54a

+ 15
- 2
conference.js Переглянути файл

@@ -88,6 +88,7 @@ import {
88 88
 import { updateSettings } from './react/features/base/settings';
89 89
 import {
90 90
     createLocalTracksF,
91
+    destroyLocalTracks,
91 92
     isLocalTrackMuted,
92 93
     replaceLocalTrack,
93 94
     trackAdded,
@@ -2466,7 +2467,11 @@ export default {
2466 2467
      */
2467 2468
     hangup(requestFeedback = false) {
2468 2469
         eventEmitter.emit(JitsiMeetConferenceEvents.BEFORE_HANGUP);
2469
-        APP.UI.removeLocalMedia();
2470
+
2471
+        APP.store.dispatch(destroyLocalTracks());
2472
+        this._localTracksInitialized = false;
2473
+        this.localVideo = null;
2474
+        this.localAudio = null;
2470 2475
 
2471 2476
         // Remove unnecessary event listeners from firing callbacks.
2472 2477
         if (this.deviceChangeListener) {
@@ -2475,6 +2480,9 @@ export default {
2475 2480
                 this.deviceChangeListener);
2476 2481
         }
2477 2482
 
2483
+        APP.UI.removeAllListeners();
2484
+        APP.remoteControl.removeAllListeners();
2485
+
2478 2486
         let requestFeedbackPromise;
2479 2487
 
2480 2488
         if (requestFeedback) {
@@ -2508,7 +2516,12 @@ export default {
2508 2516
     leaveRoomAndDisconnect() {
2509 2517
         APP.store.dispatch(conferenceWillLeave(room));
2510 2518
 
2511
-        return room.leave().then(disconnect, disconnect);
2519
+        return room.leave()
2520
+            .then(disconnect, disconnect)
2521
+            .then(() => {
2522
+                this._room = undefined;
2523
+                room = undefined;
2524
+            });
2512 2525
     },
2513 2526
 
2514 2527
     /**

+ 9
- 19
modules/UI/UI.js Переглянути файл

@@ -18,7 +18,6 @@ import {
18 18
     getLocalParticipant,
19 19
     showParticipantJoinedNotification
20 20
 } from '../../react/features/base/participants';
21
-import { destroyLocalTracks } from '../../react/features/base/tracks';
22 21
 import { toggleChat } from '../../react/features/chat';
23 22
 import { openDisplayNamePrompt } from '../../react/features/display-name';
24 23
 import { setEtherpadHasInitialzied } from '../../react/features/etherpad';
@@ -294,12 +293,6 @@ UI.start = function() {
294 293
 UI.registerListeners
295 294
     = () => UIListeners.forEach((value, key) => UI.addListener(key, value));
296 295
 
297
-/**
298
- * Unregister some UI event listeners.
299
- */
300
-UI.unregisterListeners
301
-    = () => UIListeners.forEach((value, key) => UI.removeListener(key, value));
302
-
303 296
 /**
304 297
  * Setup some DOM event listeners.
305 298
  */
@@ -577,6 +570,15 @@ UI.addListener = function(type, listener) {
577 570
     eventEmitter.on(type, listener);
578 571
 };
579 572
 
573
+/**
574
+ * Removes the all listeners for all events.
575
+ *
576
+ * @returns {void}
577
+ */
578
+UI.removeAllListeners = function() {
579
+    eventEmitter.removeAllListeners();
580
+};
581
+
580 582
 /**
581 583
  * Removes the given listener for the given type of event.
582 584
  *
@@ -968,18 +970,6 @@ UI.setLocalRemoteControlActiveChanged = function() {
968 970
     VideoLayout.setLocalRemoteControlActiveChanged();
969 971
 };
970 972
 
971
-/**
972
- * Remove media tracks and UI elements so the user no longer sees media in the
973
- * UI. The intent is to provide a feeling that the meeting has ended.
974
- *
975
- * @returns {void}
976
- */
977
-UI.removeLocalMedia = function() {
978
-    APP.store.dispatch(destroyLocalTracks());
979
-    VideoLayout.resetLargeVideo();
980
-    $('#videospace').hide();
981
-};
982
-
983 973
 // TODO: Export every function separately. For now there is no point of doing
984 974
 // this because we are importing everything.
985 975
 export default UI;

+ 0
- 12
modules/UI/shared_video/SharedVideoThumb.js Переглянути файл

@@ -64,18 +64,6 @@ SharedVideoThumb.prototype.createContainer = function(spanId) {
64 64
     return container;
65 65
 };
66 66
 
67
-/**
68
- * Removes RemoteVideo from the page.
69
- */
70
-SharedVideoThumb.prototype.remove = function() {
71
-    logger.log('Remove shared video thumb', this.id);
72
-
73
-    // Remove whole container
74
-    if (this.container.parentNode) {
75
-        this.container.parentNode.removeChild(this.container);
76
-    }
77
-};
78
-
79 67
 /**
80 68
  * Sets the display name for the thumb.
81 69
  */

+ 2
- 0
modules/UI/videolayout/LargeVideoManager.js Переглянути файл

@@ -108,6 +108,8 @@ export default class LargeVideoManager {
108 108
             this._onVideoResolutionUpdate);
109 109
 
110 110
         this.removePresenceLabel();
111
+
112
+        this.$container.css({ display: 'none' });
111 113
     }
112 114
 
113 115
     /**

+ 1
- 24
modules/UI/videolayout/RemoteVideo.js Переглянути файл

@@ -437,33 +437,10 @@ RemoteVideo.prototype.updateConnectionStatusIndicator = function() {
437 437
  * Removes RemoteVideo from the page.
438 438
  */
439 439
 RemoteVideo.prototype.remove = function() {
440
-    logger.log('Remove thumbnail', this.id);
441
-
442
-    this.removeAudioLevelIndicator();
443
-
444
-    const toolbarContainer
445
-        = this.container.querySelector('.videocontainer__toolbar');
446
-
447
-    if (toolbarContainer) {
448
-        ReactDOM.unmountComponentAtNode(toolbarContainer);
449
-    }
450
-
451
-    this.removeConnectionIndicator();
452
-
453
-    this.removeDisplayName();
454
-
455
-    this.removeAvatar();
440
+    SmallVideo.prototype.remove.call(this);
456 441
 
457 442
     this.removePresenceLabel();
458
-
459
-    this._unmountIndicators();
460
-
461 443
     this.removeRemoteVideoMenu();
462
-
463
-    // Remove whole container
464
-    if (this.container.parentNode) {
465
-        this.container.parentNode.removeChild(this.container);
466
-    }
467 444
 };
468 445
 
469 446
 RemoteVideo.prototype.waitForPlayback = function(streamElement, stream) {

+ 32
- 0
modules/UI/videolayout/SmallVideo.js Переглянути файл

@@ -745,6 +745,37 @@ SmallVideo.prototype.initBrowserSpecificProperties = function() {
745 745
     }
746 746
 };
747 747
 
748
+/**
749
+ * Cleans up components on {@code SmallVideo} and removes itself from the DOM.
750
+ *
751
+ * @returns {void}
752
+ */
753
+SmallVideo.prototype.remove = function() {
754
+    logger.log('Remove thumbnail', this.id);
755
+
756
+    this.removeAudioLevelIndicator();
757
+
758
+    const toolbarContainer
759
+        = this.container.querySelector('.videocontainer__toolbar');
760
+
761
+    if (toolbarContainer) {
762
+        ReactDOM.unmountComponentAtNode(toolbarContainer);
763
+    }
764
+
765
+    this.removeConnectionIndicator();
766
+
767
+    this.removeDisplayName();
768
+
769
+    this.removeAvatar();
770
+
771
+    this._unmountIndicators();
772
+
773
+    // Remove whole container
774
+    if (this.container.parentNode) {
775
+        this.container.parentNode.removeChild(this.container);
776
+    }
777
+};
778
+
748 779
 /**
749 780
  * Helper function for re-rendering multiple react components of the small
750 781
  * video.
@@ -938,4 +969,5 @@ SmallVideo.prototype._onPopoverHover = function(popoverIsHovered) {
938 969
     this.updateView();
939 970
 };
940 971
 
972
+
941 973
 export default SmallVideo;

+ 43
- 11
modules/UI/videolayout/VideoLayout.js Переглянути файл

@@ -133,29 +133,27 @@ const VideoLayout = {
133 133
     },
134 134
 
135 135
     /**
136
-     * Cleans up any existing largeVideo instance.
136
+     * Registering listeners for UI events in Video layout component.
137 137
      *
138 138
      * @returns {void}
139 139
      */
140
-    resetLargeVideo() {
141
-        if (largeVideo) {
142
-            largeVideo.destroy();
143
-        }
144
-        largeVideo = null;
140
+    registerListeners() {
141
+        eventEmitter.addListener(UIEvents.LOCAL_FLIPX_CHANGED,
142
+            onLocalFlipXChanged);
145 143
     },
146 144
 
147 145
     /**
148
-     * Registering listeners for UI events in Video layout component.
146
+     * Cleans up state of this singleton {@code VideoLayout}.
149 147
      *
150 148
      * @returns {void}
151 149
      */
152
-    registerListeners() {
153
-        eventEmitter.addListener(UIEvents.LOCAL_FLIPX_CHANGED,
154
-            onLocalFlipXChanged);
150
+    reset() {
151
+        this._resetLargeVideo();
152
+        this._resetFilmstrip();
155 153
     },
156 154
 
157 155
     initLargeVideo() {
158
-        this.resetLargeVideo();
156
+        this._resetLargeVideo();
159 157
 
160 158
         largeVideo = new LargeVideoManager(eventEmitter);
161 159
         if (localFlipX) {
@@ -1165,6 +1163,40 @@ const VideoLayout = {
1165 1163
         );
1166 1164
     },
1167 1165
 
1166
+    /**
1167
+     * Cleans up any existing largeVideo instance.
1168
+     *
1169
+     * @private
1170
+     * @returns {void}
1171
+     */
1172
+    _resetLargeVideo() {
1173
+        if (largeVideo) {
1174
+            largeVideo.destroy();
1175
+        }
1176
+
1177
+        largeVideo = null;
1178
+    },
1179
+
1180
+    /**
1181
+     * Cleans up filmstrip state. While a separate {@code Filmstrip} exists, its
1182
+     * implementation is mainly for querying and manipulating the DOM while
1183
+     * state mostly remains in {@code VideoLayout}.
1184
+     *
1185
+     * @private
1186
+     * @returns {void}
1187
+     */
1188
+    _resetFilmstrip() {
1189
+        Object.keys(remoteVideos).forEach(remoteVideoId => {
1190
+            this.removeParticipantContainer(remoteVideoId);
1191
+            delete remoteVideos[remoteVideoId];
1192
+        });
1193
+
1194
+        if (localVideoThumbnail) {
1195
+            localVideoThumbnail.remove();
1196
+            localVideoThumbnail = null;
1197
+        }
1198
+    },
1199
+
1168 1200
     /**
1169 1201
      * Triggers an update of large video if the passed in participant is
1170 1202
      * currently displayed on large video.

+ 9
- 0
react/features/chat/actionTypes.js Переглянути файл

@@ -13,6 +13,15 @@
13 13
  */
14 14
 export const ADD_MESSAGE = Symbol('ADD_MESSAGE');
15 15
 
16
+/**
17
+ * The type of the action which signals to remove all saved chat messages.
18
+ *
19
+ * {
20
+ *     type: CLEAR_MESSAGES
21
+ * }
22
+ */
23
+export const CLEAR_MESSAGES = Symbol('CLEAR_MESSAGES');
24
+
16 25
 /**
17 26
  * The type of the action which signals a send a chat message to everyone in the
18 27
  * conference.

+ 19
- 3
react/features/chat/actions.js Переглянути файл

@@ -1,6 +1,9 @@
1
-import { ADD_MESSAGE, SEND_MESSAGE, TOGGLE_CHAT } from './actionTypes';
2
-
3
-/* eslint-disable max-params */
1
+import {
2
+    ADD_MESSAGE,
3
+    CLEAR_MESSAGES,
4
+    SEND_MESSAGE,
5
+    TOGGLE_CHAT
6
+} from './actionTypes';
4 7
 
5 8
 /**
6 9
  * Adds a chat message to the collection of messages.
@@ -31,6 +34,19 @@ export function addMessage(messageDetails) {
31 34
     };
32 35
 }
33 36
 
37
+/**
38
+ * Removes all stored chat messages.
39
+ *
40
+ * @returns {{
41
+ *     type: CLEAR_MESSAGES
42
+ * }}
43
+ */
44
+export function clearMessages() {
45
+    return {
46
+        type: CLEAR_MESSAGES
47
+    };
48
+}
49
+
34 50
 /**
35 51
  * Sends a chat message to everyone in the conference.
36 52
  *

+ 6
- 2
react/features/chat/middleware.js Переглянути файл

@@ -3,7 +3,7 @@
3 3
 import UIUtil from '../../../modules/UI/util/UIUtil';
4 4
 
5 5
 import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../base/app';
6
-import { CONFERENCE_JOINED } from '../base/conference';
6
+import { CONFERENCE_JOINED, CONFERENCE_WILL_LEAVE } from '../base/conference';
7 7
 import { JitsiConferenceEvents } from '../base/lib-jitsi-meet';
8 8
 import { getParticipantById } from '../base/participants';
9 9
 import { MiddlewareRegistry } from '../base/redux';
@@ -11,7 +11,7 @@ import { playSound, registerSound, unregisterSound } from '../base/sounds';
11 11
 import { isButtonEnabled, showToolbox } from '../toolbox';
12 12
 
13 13
 import { SEND_MESSAGE } from './actionTypes';
14
-import { addMessage } from './actions';
14
+import { addMessage, clearMessages } from './actions';
15 15
 import { INCOMING_MSG_SOUND_ID } from './constants';
16 16
 import { INCOMING_MSG_SOUND_FILE } from './sounds';
17 17
 
@@ -46,6 +46,10 @@ MiddlewareRegistry.register(store => next => action => {
46 46
             || _addChatMsgListener(action.conference, store);
47 47
         break;
48 48
 
49
+    case CONFERENCE_WILL_LEAVE:
50
+        store.dispatch(clearMessages());
51
+        break;
52
+
49 53
     case SEND_MESSAGE:
50 54
         if (typeof APP !== 'undefined') {
51 55
             const { conference } = store.getState()['features/base/conference'];

+ 8
- 1
react/features/chat/reducer.js Переглянути файл

@@ -2,7 +2,7 @@
2 2
 
3 3
 import { ReducerRegistry } from '../base/redux';
4 4
 
5
-import { ADD_MESSAGE, TOGGLE_CHAT } from './actionTypes';
5
+import { ADD_MESSAGE, CLEAR_MESSAGES, TOGGLE_CHAT } from './actionTypes';
6 6
 
7 7
 const DEFAULT_STATE = {
8 8
     isOpen: false,
@@ -33,6 +33,13 @@ ReducerRegistry.register('features/chat', (state = DEFAULT_STATE, action) => {
33 33
         };
34 34
     }
35 35
 
36
+    case CLEAR_MESSAGES:
37
+        return {
38
+            ...state,
39
+            lastReadMessage: undefined,
40
+            messages: []
41
+        };
42
+
36 43
     case TOGGLE_CHAT:
37 44
         return {
38 45
             ...state,

+ 0
- 1
react/features/conference/components/Conference.web.js Переглянути файл

@@ -182,7 +182,6 @@ class Conference extends Component<Props> {
182 182
      * @inheritdoc
183 183
      */
184 184
     componentWillUnmount() {
185
-        APP.UI.unregisterListeners();
186 185
         APP.UI.unbindEvents();
187 186
 
188 187
         FULL_SCREEN_EVENTS.forEach(name =>

+ 5
- 1
react/features/video-layout/middleware.web.js Переглянути файл

@@ -3,7 +3,7 @@
3 3
 import VideoLayout from '../../../modules/UI/videolayout/VideoLayout.js';
4 4
 import UIEvents from '../../../service/UI/UIEvents';
5 5
 
6
-import { CONFERENCE_JOINED } from '../base/conference';
6
+import { CONFERENCE_JOINED, CONFERENCE_WILL_LEAVE } from '../base/conference';
7 7
 import {
8 8
     DOMINANT_SPEAKER_CHANGED,
9 9
     PARTICIPANT_JOINED,
@@ -40,6 +40,10 @@ MiddlewareRegistry.register(store => next => action => {
40 40
         VideoLayout.mucJoined();
41 41
         break;
42 42
 
43
+    case CONFERENCE_WILL_LEAVE:
44
+        VideoLayout.reset();
45
+        break;
46
+
43 47
     case PARTICIPANT_JOINED:
44 48
         if (!action.participant.local) {
45 49
             VideoLayout.addRemoteParticipantContainer(

Завантаження…
Відмінити
Зберегти