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 { updateSettings } from './react/features/base/settings';
|
||||||
import {
|
import {
|
||||||
createLocalTracksF,
|
createLocalTracksF,
|
||||||
|
destroyLocalTracks,
|
||||||
isLocalTrackMuted,
|
isLocalTrackMuted,
|
||||||
replaceLocalTrack,
|
replaceLocalTrack,
|
||||||
trackAdded,
|
trackAdded,
|
||||||
|
@ -2466,7 +2467,11 @@ export default {
|
||||||
*/
|
*/
|
||||||
hangup(requestFeedback = false) {
|
hangup(requestFeedback = false) {
|
||||||
eventEmitter.emit(JitsiMeetConferenceEvents.BEFORE_HANGUP);
|
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.
|
// Remove unnecessary event listeners from firing callbacks.
|
||||||
if (this.deviceChangeListener) {
|
if (this.deviceChangeListener) {
|
||||||
|
@ -2475,6 +2480,9 @@ export default {
|
||||||
this.deviceChangeListener);
|
this.deviceChangeListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
APP.UI.removeAllListeners();
|
||||||
|
APP.remoteControl.removeAllListeners();
|
||||||
|
|
||||||
let requestFeedbackPromise;
|
let requestFeedbackPromise;
|
||||||
|
|
||||||
if (requestFeedback) {
|
if (requestFeedback) {
|
||||||
|
@ -2508,7 +2516,12 @@ export default {
|
||||||
leaveRoomAndDisconnect() {
|
leaveRoomAndDisconnect() {
|
||||||
APP.store.dispatch(conferenceWillLeave(room));
|
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,
|
getLocalParticipant,
|
||||||
showParticipantJoinedNotification
|
showParticipantJoinedNotification
|
||||||
} from '../../react/features/base/participants';
|
} from '../../react/features/base/participants';
|
||||||
import { destroyLocalTracks } from '../../react/features/base/tracks';
|
|
||||||
import { toggleChat } from '../../react/features/chat';
|
import { toggleChat } from '../../react/features/chat';
|
||||||
import { openDisplayNamePrompt } from '../../react/features/display-name';
|
import { openDisplayNamePrompt } from '../../react/features/display-name';
|
||||||
import { setEtherpadHasInitialzied } from '../../react/features/etherpad';
|
import { setEtherpadHasInitialzied } from '../../react/features/etherpad';
|
||||||
|
@ -294,12 +293,6 @@ UI.start = function() {
|
||||||
UI.registerListeners
|
UI.registerListeners
|
||||||
= () => UIListeners.forEach((value, key) => UI.addListener(key, value));
|
= () => 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.
|
* Setup some DOM event listeners.
|
||||||
*/
|
*/
|
||||||
|
@ -577,6 +570,15 @@ UI.addListener = function(type, listener) {
|
||||||
eventEmitter.on(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.
|
* Removes the given listener for the given type of event.
|
||||||
*
|
*
|
||||||
|
@ -968,18 +970,6 @@ UI.setLocalRemoteControlActiveChanged = function() {
|
||||||
VideoLayout.setLocalRemoteControlActiveChanged();
|
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
|
// TODO: Export every function separately. For now there is no point of doing
|
||||||
// this because we are importing everything.
|
// this because we are importing everything.
|
||||||
export default UI;
|
export default UI;
|
||||||
|
|
|
@ -64,18 +64,6 @@ SharedVideoThumb.prototype.createContainer = function(spanId) {
|
||||||
return container;
|
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.
|
* Sets the display name for the thumb.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -108,6 +108,8 @@ export default class LargeVideoManager {
|
||||||
this._onVideoResolutionUpdate);
|
this._onVideoResolutionUpdate);
|
||||||
|
|
||||||
this.removePresenceLabel();
|
this.removePresenceLabel();
|
||||||
|
|
||||||
|
this.$container.css({ display: 'none' });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -437,33 +437,10 @@ RemoteVideo.prototype.updateConnectionStatusIndicator = function() {
|
||||||
* Removes RemoteVideo from the page.
|
* Removes RemoteVideo from the page.
|
||||||
*/
|
*/
|
||||||
RemoteVideo.prototype.remove = function() {
|
RemoteVideo.prototype.remove = function() {
|
||||||
logger.log('Remove thumbnail', this.id);
|
SmallVideo.prototype.remove.call(this);
|
||||||
|
|
||||||
this.removeAudioLevelIndicator();
|
|
||||||
|
|
||||||
const toolbarContainer
|
|
||||||
= this.container.querySelector('.videocontainer__toolbar');
|
|
||||||
|
|
||||||
if (toolbarContainer) {
|
|
||||||
ReactDOM.unmountComponentAtNode(toolbarContainer);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.removeConnectionIndicator();
|
|
||||||
|
|
||||||
this.removeDisplayName();
|
|
||||||
|
|
||||||
this.removeAvatar();
|
|
||||||
|
|
||||||
this.removePresenceLabel();
|
this.removePresenceLabel();
|
||||||
|
|
||||||
this._unmountIndicators();
|
|
||||||
|
|
||||||
this.removeRemoteVideoMenu();
|
this.removeRemoteVideoMenu();
|
||||||
|
|
||||||
// Remove whole container
|
|
||||||
if (this.container.parentNode) {
|
|
||||||
this.container.parentNode.removeChild(this.container);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
RemoteVideo.prototype.waitForPlayback = function(streamElement, stream) {
|
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
|
* Helper function for re-rendering multiple react components of the small
|
||||||
* video.
|
* video.
|
||||||
|
@ -938,4 +969,5 @@ SmallVideo.prototype._onPopoverHover = function(popoverIsHovered) {
|
||||||
this.updateView();
|
this.updateView();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export default SmallVideo;
|
export default SmallVideo;
|
||||||
|
|
|
@ -132,18 +132,6 @@ const VideoLayout = {
|
||||||
this.registerListeners();
|
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.
|
* Registering listeners for UI events in Video layout component.
|
||||||
*
|
*
|
||||||
|
@ -154,8 +142,18 @@ const VideoLayout = {
|
||||||
onLocalFlipXChanged);
|
onLocalFlipXChanged);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleans up state of this singleton {@code VideoLayout}.
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
reset() {
|
||||||
|
this._resetLargeVideo();
|
||||||
|
this._resetFilmstrip();
|
||||||
|
},
|
||||||
|
|
||||||
initLargeVideo() {
|
initLargeVideo() {
|
||||||
this.resetLargeVideo();
|
this._resetLargeVideo();
|
||||||
|
|
||||||
largeVideo = new LargeVideoManager(eventEmitter);
|
largeVideo = new LargeVideoManager(eventEmitter);
|
||||||
if (localFlipX) {
|
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
|
* Triggers an update of large video if the passed in participant is
|
||||||
* currently displayed on large video.
|
* currently displayed on large video.
|
||||||
|
|
|
@ -13,6 +13,15 @@
|
||||||
*/
|
*/
|
||||||
export const ADD_MESSAGE = Symbol('ADD_MESSAGE');
|
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
|
* The type of the action which signals a send a chat message to everyone in the
|
||||||
* conference.
|
* conference.
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
import { ADD_MESSAGE, SEND_MESSAGE, TOGGLE_CHAT } from './actionTypes';
|
import {
|
||||||
|
ADD_MESSAGE,
|
||||||
/* eslint-disable max-params */
|
CLEAR_MESSAGES,
|
||||||
|
SEND_MESSAGE,
|
||||||
|
TOGGLE_CHAT
|
||||||
|
} from './actionTypes';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a chat message to the collection of messages.
|
* 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.
|
* Sends a chat message to everyone in the conference.
|
||||||
*
|
*
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import UIUtil from '../../../modules/UI/util/UIUtil';
|
import UIUtil from '../../../modules/UI/util/UIUtil';
|
||||||
|
|
||||||
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../base/app';
|
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 { JitsiConferenceEvents } from '../base/lib-jitsi-meet';
|
||||||
import { getParticipantById } from '../base/participants';
|
import { getParticipantById } from '../base/participants';
|
||||||
import { MiddlewareRegistry } from '../base/redux';
|
import { MiddlewareRegistry } from '../base/redux';
|
||||||
|
@ -11,7 +11,7 @@ import { playSound, registerSound, unregisterSound } from '../base/sounds';
|
||||||
import { isButtonEnabled, showToolbox } from '../toolbox';
|
import { isButtonEnabled, showToolbox } from '../toolbox';
|
||||||
|
|
||||||
import { SEND_MESSAGE } from './actionTypes';
|
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_ID } from './constants';
|
||||||
import { INCOMING_MSG_SOUND_FILE } from './sounds';
|
import { INCOMING_MSG_SOUND_FILE } from './sounds';
|
||||||
|
|
||||||
|
@ -46,6 +46,10 @@ MiddlewareRegistry.register(store => next => action => {
|
||||||
|| _addChatMsgListener(action.conference, store);
|
|| _addChatMsgListener(action.conference, store);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case CONFERENCE_WILL_LEAVE:
|
||||||
|
store.dispatch(clearMessages());
|
||||||
|
break;
|
||||||
|
|
||||||
case SEND_MESSAGE:
|
case SEND_MESSAGE:
|
||||||
if (typeof APP !== 'undefined') {
|
if (typeof APP !== 'undefined') {
|
||||||
const { conference } = store.getState()['features/base/conference'];
|
const { conference } = store.getState()['features/base/conference'];
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import { ReducerRegistry } from '../base/redux';
|
import { ReducerRegistry } from '../base/redux';
|
||||||
|
|
||||||
import { ADD_MESSAGE, TOGGLE_CHAT } from './actionTypes';
|
import { ADD_MESSAGE, CLEAR_MESSAGES, TOGGLE_CHAT } from './actionTypes';
|
||||||
|
|
||||||
const DEFAULT_STATE = {
|
const DEFAULT_STATE = {
|
||||||
isOpen: false,
|
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:
|
case TOGGLE_CHAT:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
|
|
|
@ -182,7 +182,6 @@ class Conference extends Component<Props> {
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
APP.UI.unregisterListeners();
|
|
||||||
APP.UI.unbindEvents();
|
APP.UI.unbindEvents();
|
||||||
|
|
||||||
FULL_SCREEN_EVENTS.forEach(name =>
|
FULL_SCREEN_EVENTS.forEach(name =>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import VideoLayout from '../../../modules/UI/videolayout/VideoLayout.js';
|
import VideoLayout from '../../../modules/UI/videolayout/VideoLayout.js';
|
||||||
import UIEvents from '../../../service/UI/UIEvents';
|
import UIEvents from '../../../service/UI/UIEvents';
|
||||||
|
|
||||||
import { CONFERENCE_JOINED } from '../base/conference';
|
import { CONFERENCE_JOINED, CONFERENCE_WILL_LEAVE } from '../base/conference';
|
||||||
import {
|
import {
|
||||||
DOMINANT_SPEAKER_CHANGED,
|
DOMINANT_SPEAKER_CHANGED,
|
||||||
PARTICIPANT_JOINED,
|
PARTICIPANT_JOINED,
|
||||||
|
@ -40,6 +40,10 @@ MiddlewareRegistry.register(store => next => action => {
|
||||||
VideoLayout.mucJoined();
|
VideoLayout.mucJoined();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case CONFERENCE_WILL_LEAVE:
|
||||||
|
VideoLayout.reset();
|
||||||
|
break;
|
||||||
|
|
||||||
case PARTICIPANT_JOINED:
|
case PARTICIPANT_JOINED:
|
||||||
if (!action.participant.local) {
|
if (!action.participant.local) {
|
||||||
VideoLayout.addRemoteParticipantContainer(
|
VideoLayout.addRemoteParticipantContainer(
|
||||||
|
|
Loading…
Reference in New Issue