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:
parent
9215b1e8b2
commit
14cc4ea54a
|
@ -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;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -108,6 +108,8 @@ export default class LargeVideoManager {
|
|||
this._onVideoResolutionUpdate);
|
||||
|
||||
this.removePresenceLabel();
|
||||
|
||||
this.$container.css({ display: 'none' });
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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'];
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -182,7 +182,6 @@ class Conference extends Component<Props> {
|
|||
* @inheritdoc
|
||||
*/
|
||||
componentWillUnmount() {
|
||||
APP.UI.unregisterListeners();
|
||||
APP.UI.unbindEvents();
|
||||
|
||||
FULL_SCREEN_EVENTS.forEach(name =>
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Reference in New Issue