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.
This commit is contained in:
Leonard Kim 2019-01-01 13:19:34 -08:00
parent 9215b1e8b2
commit 14cc4ea54a
13 changed files with 151 additions and 78 deletions

View File

@ -88,6 +88,7 @@ import {
import { updateSettings } from './react/features/base/settings';
import {
createLocalTracksF,
destroyLocalTracks,
isLocalTrackMuted,
replaceLocalTrack,
trackAdded,
@ -2466,7 +2467,11 @@ export default {
*/
hangup(requestFeedback = false) {
eventEmitter.emit(JitsiMeetConferenceEvents.BEFORE_HANGUP);
APP.UI.removeLocalMedia();
APP.store.dispatch(destroyLocalTracks());
this._localTracksInitialized = false;
this.localVideo = null;
this.localAudio = null;
// Remove unnecessary event listeners from firing callbacks.
if (this.deviceChangeListener) {
@ -2475,6 +2480,9 @@ export default {
this.deviceChangeListener);
}
APP.UI.removeAllListeners();
APP.remoteControl.removeAllListeners();
let requestFeedbackPromise;
if (requestFeedback) {
@ -2508,7 +2516,12 @@ export default {
leaveRoomAndDisconnect() {
APP.store.dispatch(conferenceWillLeave(room));
return room.leave().then(disconnect, disconnect);
return room.leave()
.then(disconnect, disconnect)
.then(() => {
this._room = undefined;
room = undefined;
});
},
/**

View File

@ -18,7 +18,6 @@ import {
getLocalParticipant,
showParticipantJoinedNotification
} from '../../react/features/base/participants';
import { destroyLocalTracks } from '../../react/features/base/tracks';
import { toggleChat } from '../../react/features/chat';
import { openDisplayNamePrompt } from '../../react/features/display-name';
import { setEtherpadHasInitialzied } from '../../react/features/etherpad';
@ -294,12 +293,6 @@ UI.start = function() {
UI.registerListeners
= () => UIListeners.forEach((value, key) => UI.addListener(key, value));
/**
* Unregister some UI event listeners.
*/
UI.unregisterListeners
= () => UIListeners.forEach((value, key) => UI.removeListener(key, value));
/**
* Setup some DOM event listeners.
*/
@ -577,6 +570,15 @@ UI.addListener = function(type, listener) {
eventEmitter.on(type, listener);
};
/**
* Removes the all listeners for all events.
*
* @returns {void}
*/
UI.removeAllListeners = function() {
eventEmitter.removeAllListeners();
};
/**
* Removes the given listener for the given type of event.
*
@ -968,18 +970,6 @@ UI.setLocalRemoteControlActiveChanged = function() {
VideoLayout.setLocalRemoteControlActiveChanged();
};
/**
* Remove media tracks and UI elements so the user no longer sees media in the
* UI. The intent is to provide a feeling that the meeting has ended.
*
* @returns {void}
*/
UI.removeLocalMedia = function() {
APP.store.dispatch(destroyLocalTracks());
VideoLayout.resetLargeVideo();
$('#videospace').hide();
};
// TODO: Export every function separately. For now there is no point of doing
// this because we are importing everything.
export default UI;

View File

@ -64,18 +64,6 @@ SharedVideoThumb.prototype.createContainer = function(spanId) {
return container;
};
/**
* Removes RemoteVideo from the page.
*/
SharedVideoThumb.prototype.remove = function() {
logger.log('Remove shared video thumb', this.id);
// Remove whole container
if (this.container.parentNode) {
this.container.parentNode.removeChild(this.container);
}
};
/**
* Sets the display name for the thumb.
*/

View File

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

View File

@ -437,33 +437,10 @@ RemoteVideo.prototype.updateConnectionStatusIndicator = function() {
* Removes RemoteVideo from the page.
*/
RemoteVideo.prototype.remove = function() {
logger.log('Remove thumbnail', this.id);
this.removeAudioLevelIndicator();
const toolbarContainer
= this.container.querySelector('.videocontainer__toolbar');
if (toolbarContainer) {
ReactDOM.unmountComponentAtNode(toolbarContainer);
}
this.removeConnectionIndicator();
this.removeDisplayName();
this.removeAvatar();
SmallVideo.prototype.remove.call(this);
this.removePresenceLabel();
this._unmountIndicators();
this.removeRemoteVideoMenu();
// Remove whole container
if (this.container.parentNode) {
this.container.parentNode.removeChild(this.container);
}
};
RemoteVideo.prototype.waitForPlayback = function(streamElement, stream) {

View File

@ -745,6 +745,37 @@ SmallVideo.prototype.initBrowserSpecificProperties = function() {
}
};
/**
* Cleans up components on {@code SmallVideo} and removes itself from the DOM.
*
* @returns {void}
*/
SmallVideo.prototype.remove = function() {
logger.log('Remove thumbnail', this.id);
this.removeAudioLevelIndicator();
const toolbarContainer
= this.container.querySelector('.videocontainer__toolbar');
if (toolbarContainer) {
ReactDOM.unmountComponentAtNode(toolbarContainer);
}
this.removeConnectionIndicator();
this.removeDisplayName();
this.removeAvatar();
this._unmountIndicators();
// Remove whole container
if (this.container.parentNode) {
this.container.parentNode.removeChild(this.container);
}
};
/**
* Helper function for re-rendering multiple react components of the small
* video.
@ -938,4 +969,5 @@ SmallVideo.prototype._onPopoverHover = function(popoverIsHovered) {
this.updateView();
};
export default SmallVideo;

View File

@ -132,18 +132,6 @@ const VideoLayout = {
this.registerListeners();
},
/**
* Cleans up any existing largeVideo instance.
*
* @returns {void}
*/
resetLargeVideo() {
if (largeVideo) {
largeVideo.destroy();
}
largeVideo = null;
},
/**
* Registering listeners for UI events in Video layout component.
*
@ -154,8 +142,18 @@ const VideoLayout = {
onLocalFlipXChanged);
},
/**
* Cleans up state of this singleton {@code VideoLayout}.
*
* @returns {void}
*/
reset() {
this._resetLargeVideo();
this._resetFilmstrip();
},
initLargeVideo() {
this.resetLargeVideo();
this._resetLargeVideo();
largeVideo = new LargeVideoManager(eventEmitter);
if (localFlipX) {
@ -1165,6 +1163,40 @@ const VideoLayout = {
);
},
/**
* Cleans up any existing largeVideo instance.
*
* @private
* @returns {void}
*/
_resetLargeVideo() {
if (largeVideo) {
largeVideo.destroy();
}
largeVideo = null;
},
/**
* Cleans up filmstrip state. While a separate {@code Filmstrip} exists, its
* implementation is mainly for querying and manipulating the DOM while
* state mostly remains in {@code VideoLayout}.
*
* @private
* @returns {void}
*/
_resetFilmstrip() {
Object.keys(remoteVideos).forEach(remoteVideoId => {
this.removeParticipantContainer(remoteVideoId);
delete remoteVideos[remoteVideoId];
});
if (localVideoThumbnail) {
localVideoThumbnail.remove();
localVideoThumbnail = null;
}
},
/**
* Triggers an update of large video if the passed in participant is
* currently displayed on large video.

View File

@ -13,6 +13,15 @@
*/
export const ADD_MESSAGE = Symbol('ADD_MESSAGE');
/**
* The type of the action which signals to remove all saved chat messages.
*
* {
* type: CLEAR_MESSAGES
* }
*/
export const CLEAR_MESSAGES = Symbol('CLEAR_MESSAGES');
/**
* The type of the action which signals a send a chat message to everyone in the
* conference.

View File

@ -1,6 +1,9 @@
import { ADD_MESSAGE, SEND_MESSAGE, TOGGLE_CHAT } from './actionTypes';
/* eslint-disable max-params */
import {
ADD_MESSAGE,
CLEAR_MESSAGES,
SEND_MESSAGE,
TOGGLE_CHAT
} from './actionTypes';
/**
* Adds a chat message to the collection of messages.
@ -31,6 +34,19 @@ export function addMessage(messageDetails) {
};
}
/**
* Removes all stored chat messages.
*
* @returns {{
* type: CLEAR_MESSAGES
* }}
*/
export function clearMessages() {
return {
type: CLEAR_MESSAGES
};
}
/**
* Sends a chat message to everyone in the conference.
*

View File

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

View File

@ -2,7 +2,7 @@
import { ReducerRegistry } from '../base/redux';
import { ADD_MESSAGE, TOGGLE_CHAT } from './actionTypes';
import { ADD_MESSAGE, CLEAR_MESSAGES, TOGGLE_CHAT } from './actionTypes';
const DEFAULT_STATE = {
isOpen: false,
@ -33,6 +33,13 @@ ReducerRegistry.register('features/chat', (state = DEFAULT_STATE, action) => {
};
}
case CLEAR_MESSAGES:
return {
...state,
lastReadMessage: undefined,
messages: []
};
case TOGGLE_CHAT:
return {
...state,

View File

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

View File

@ -3,7 +3,7 @@
import VideoLayout from '../../../modules/UI/videolayout/VideoLayout.js';
import UIEvents from '../../../service/UI/UIEvents';
import { CONFERENCE_JOINED } from '../base/conference';
import { CONFERENCE_JOINED, CONFERENCE_WILL_LEAVE } from '../base/conference';
import {
DOMINANT_SPEAKER_CHANGED,
PARTICIPANT_JOINED,
@ -40,6 +40,10 @@ MiddlewareRegistry.register(store => next => action => {
VideoLayout.mucJoined();
break;
case CONFERENCE_WILL_LEAVE:
VideoLayout.reset();
break;
case PARTICIPANT_JOINED:
if (!action.participant.local) {
VideoLayout.addRemoteParticipantContainer(