mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2025-12-30 11:22:31 +00:00
Also unify the mobile and web features into one, even though internally they still have separate ways to enable the functionality.
516 lines
13 KiB
JavaScript
516 lines
13 KiB
JavaScript
/* global APP, $, config */
|
|
|
|
|
|
const UI = {};
|
|
|
|
import EventEmitter from 'events';
|
|
import Logger from 'jitsi-meet-logger';
|
|
|
|
import { isMobileBrowser } from '../../react/features/base/environment/utils';
|
|
import { toggleChat } from '../../react/features/chat';
|
|
import { setDocumentUrl } from '../../react/features/etherpad';
|
|
import { setFilmstripVisible } from '../../react/features/filmstrip';
|
|
import { joinLeaveNotificationsDisabled, setNotificationsEnabled } from '../../react/features/notifications';
|
|
import {
|
|
dockToolbox,
|
|
setToolboxEnabled,
|
|
showToolbox
|
|
} from '../../react/features/toolbox/actions.web';
|
|
import UIEvents from '../../service/UI/UIEvents';
|
|
|
|
import EtherpadManager from './etherpad/Etherpad';
|
|
import SharedVideoManager from './shared_video/SharedVideo';
|
|
import messageHandler from './util/MessageHandler';
|
|
import UIUtil from './util/UIUtil';
|
|
import VideoLayout from './videolayout/VideoLayout';
|
|
|
|
const logger = Logger.getLogger(__filename);
|
|
|
|
UI.messageHandler = messageHandler;
|
|
|
|
const eventEmitter = new EventEmitter();
|
|
|
|
UI.eventEmitter = eventEmitter;
|
|
|
|
let etherpadManager;
|
|
let sharedVideoManager;
|
|
|
|
const UIListeners = new Map([
|
|
[
|
|
UIEvents.ETHERPAD_CLICKED,
|
|
() => etherpadManager && etherpadManager.toggleEtherpad()
|
|
], [
|
|
UIEvents.SHARED_VIDEO_CLICKED,
|
|
() => sharedVideoManager && sharedVideoManager.toggleSharedVideo()
|
|
], [
|
|
UIEvents.TOGGLE_FILMSTRIP,
|
|
() => UI.toggleFilmstrip()
|
|
]
|
|
]);
|
|
|
|
/**
|
|
* Indicates if we're currently in full screen mode.
|
|
*
|
|
* @return {boolean} {true} to indicate that we're currently in full screen
|
|
* mode, {false} otherwise
|
|
*/
|
|
UI.isFullScreen = function() {
|
|
return UIUtil.isFullScreen();
|
|
};
|
|
|
|
/**
|
|
* Returns true if there is a shared video which is being shown (?).
|
|
* @returns {boolean} - true if there is a shared video which is being shown.
|
|
*/
|
|
UI.isSharedVideoShown = function() {
|
|
return Boolean(sharedVideoManager && sharedVideoManager.isSharedVideoShown);
|
|
};
|
|
|
|
/**
|
|
* Notify user that server has shut down.
|
|
*/
|
|
UI.notifyGracefulShutdown = function() {
|
|
messageHandler.showError({
|
|
descriptionKey: 'dialog.gracefulShutdown',
|
|
titleKey: 'dialog.serviceUnavailable'
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Notify user that reservation error happened.
|
|
*/
|
|
UI.notifyReservationError = function(code, msg) {
|
|
messageHandler.showError({
|
|
descriptionArguments: {
|
|
code,
|
|
msg
|
|
},
|
|
descriptionKey: 'dialog.reservationErrorMsg',
|
|
titleKey: 'dialog.reservationError'
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Initialize conference UI.
|
|
*/
|
|
UI.initConference = function() {
|
|
UI.showToolbar();
|
|
};
|
|
|
|
/**
|
|
* Returns the shared document manager object.
|
|
* @return {EtherpadManager} the shared document manager object
|
|
*/
|
|
UI.getSharedVideoManager = function() {
|
|
return sharedVideoManager;
|
|
};
|
|
|
|
/**
|
|
* Starts the UI module and initializes all related components.
|
|
*
|
|
* @returns {boolean} true if the UI is ready and the conference should be
|
|
* established, false - otherwise (for example in the case of welcome page)
|
|
*/
|
|
UI.start = function() {
|
|
// Set the defaults for prompt dialogs.
|
|
$.prompt.setDefaults({ persistent: false });
|
|
|
|
VideoLayout.init(eventEmitter);
|
|
VideoLayout.initLargeVideo();
|
|
|
|
// Do not animate the video area on UI start (second argument passed into
|
|
// resizeVideoArea) because the animation is not visible anyway. Plus with
|
|
// the current dom layout, the quality label is part of the video layout and
|
|
// will be seen animating in.
|
|
VideoLayout.resizeVideoArea();
|
|
|
|
sharedVideoManager = new SharedVideoManager(eventEmitter);
|
|
|
|
if (isMobileBrowser()) {
|
|
$('body').addClass('mobile-browser');
|
|
} else {
|
|
$('body').addClass('desktop-browser');
|
|
}
|
|
|
|
if (config.iAmRecorder) {
|
|
// in case of iAmSipGateway keep local video visible
|
|
if (!config.iAmSipGateway) {
|
|
VideoLayout.setLocalVideoVisible(false);
|
|
APP.store.dispatch(setNotificationsEnabled(false));
|
|
}
|
|
|
|
APP.store.dispatch(setToolboxEnabled(false));
|
|
UI.messageHandler.enablePopups(false);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Setup some UI event listeners.
|
|
*/
|
|
UI.registerListeners
|
|
= () => UIListeners.forEach((value, key) => UI.addListener(key, value));
|
|
|
|
/**
|
|
* Setup some DOM event listeners.
|
|
*/
|
|
UI.bindEvents = () => {
|
|
/**
|
|
*
|
|
*/
|
|
function onResize() {
|
|
VideoLayout.onResize();
|
|
}
|
|
|
|
// Resize and reposition videos in full screen mode.
|
|
$(document).on(
|
|
'webkitfullscreenchange mozfullscreenchange fullscreenchange',
|
|
onResize);
|
|
|
|
$(window).resize(onResize);
|
|
};
|
|
|
|
/**
|
|
* Unbind some DOM event listeners.
|
|
*/
|
|
UI.unbindEvents = () => {
|
|
$(document).off(
|
|
'webkitfullscreenchange mozfullscreenchange fullscreenchange');
|
|
|
|
$(window).off('resize');
|
|
};
|
|
|
|
/**
|
|
* Show local video stream on UI.
|
|
* @param {JitsiTrack} track stream to show
|
|
*/
|
|
UI.addLocalVideoStream = track => {
|
|
VideoLayout.changeLocalVideo(track);
|
|
};
|
|
|
|
/**
|
|
* Setup and show Etherpad.
|
|
* @param {string} name etherpad id
|
|
*/
|
|
UI.initEtherpad = name => {
|
|
if (etherpadManager || !config.etherpad_base || !name) {
|
|
return;
|
|
}
|
|
logger.log('Etherpad is enabled');
|
|
|
|
etherpadManager = new EtherpadManager(eventEmitter);
|
|
|
|
const url = new URL(name, config.etherpad_base);
|
|
|
|
APP.store.dispatch(setDocumentUrl(url.toString()));
|
|
|
|
if (config.openSharedDocumentOnJoin) {
|
|
etherpadManager.toggleEtherpad();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Returns the shared document manager object.
|
|
* @return {EtherpadManager} the shared document manager object
|
|
*/
|
|
UI.getSharedDocumentManager = () => etherpadManager;
|
|
|
|
/**
|
|
* Show user on UI.
|
|
* @param {JitsiParticipant} user
|
|
*/
|
|
UI.addUser = function(user) {
|
|
const status = user.getStatus();
|
|
|
|
if (status) {
|
|
// FIXME: move updateUserStatus in participantPresenceChanged action
|
|
UI.updateUserStatus(user, status);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Update videotype for specified user.
|
|
* @param {string} id user id
|
|
* @param {string} newVideoType new videotype
|
|
*/
|
|
UI.onPeerVideoTypeChanged
|
|
= (id, newVideoType) => VideoLayout.onVideoTypeChanged(id, newVideoType);
|
|
|
|
/**
|
|
* Updates the user status.
|
|
*
|
|
* @param {JitsiParticipant} user - The user which status we need to update.
|
|
* @param {string} status - The new status.
|
|
*/
|
|
UI.updateUserStatus = (user, status) => {
|
|
const reduxState = APP.store.getState() || {};
|
|
const { calleeInfoVisible } = reduxState['features/invite'] || {};
|
|
|
|
// We hide status updates when join/leave notifications are disabled,
|
|
// as jigasi is the component with statuses and they are seen as join/leave notifications.
|
|
if (!status || calleeInfoVisible || joinLeaveNotificationsDisabled()) {
|
|
return;
|
|
}
|
|
|
|
const displayName = user.getDisplayName();
|
|
|
|
messageHandler.participantNotification(
|
|
displayName,
|
|
'',
|
|
'connected',
|
|
'dialOut.statusMessage',
|
|
{ status: UIUtil.escapeHtml(status) });
|
|
};
|
|
|
|
/**
|
|
* Toggles filmstrip.
|
|
*/
|
|
UI.toggleFilmstrip = function() {
|
|
const { visible } = APP.store.getState()['features/filmstrip'];
|
|
|
|
APP.store.dispatch(setFilmstripVisible(!visible));
|
|
};
|
|
|
|
/**
|
|
* Toggles the visibility of the chat panel.
|
|
*/
|
|
UI.toggleChat = () => APP.store.dispatch(toggleChat());
|
|
|
|
/**
|
|
* Sets muted audio state for participant
|
|
*/
|
|
UI.setAudioMuted = function(id) {
|
|
// FIXME: Maybe this can be removed!
|
|
if (APP.conference.isLocalId(id)) {
|
|
APP.conference.updateAudioIconEnabled();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Sets muted video state for participant
|
|
*/
|
|
UI.setVideoMuted = function(id) {
|
|
VideoLayout.onVideoMute(id);
|
|
if (APP.conference.isLocalId(id)) {
|
|
APP.conference.updateVideoIconEnabled();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Triggers an update of remote video and large video displays so they may pick
|
|
* up any state changes that have occurred elsewhere.
|
|
*
|
|
* @returns {void}
|
|
*/
|
|
UI.updateAllVideos = () => VideoLayout.updateAllVideos();
|
|
|
|
/**
|
|
* Adds a listener that would be notified on the given type of event.
|
|
*
|
|
* @param type the type of the event we're listening for
|
|
* @param listener a function that would be called when notified
|
|
*/
|
|
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.
|
|
*
|
|
* @param type the type of the event we're listening for
|
|
* @param listener the listener we want to remove
|
|
*/
|
|
UI.removeListener = function(type, listener) {
|
|
eventEmitter.removeListener(type, listener);
|
|
};
|
|
|
|
/**
|
|
* Emits the event of given type by specifying the parameters in options.
|
|
*
|
|
* @param type the type of the event we're emitting
|
|
* @param options the parameters for the event
|
|
*/
|
|
UI.emitEvent = (type, ...options) => eventEmitter.emit(type, ...options);
|
|
|
|
UI.clickOnVideo = videoNumber => VideoLayout.togglePin(videoNumber);
|
|
|
|
// Used by torture.
|
|
UI.showToolbar = timeout => APP.store.dispatch(showToolbox(timeout));
|
|
|
|
// Used by torture.
|
|
UI.dockToolbar = dock => APP.store.dispatch(dockToolbox(dock));
|
|
|
|
/**
|
|
* Updates the displayed avatar for participant.
|
|
*
|
|
* @param {string} id - User id whose avatar should be updated.
|
|
* @param {string} avatarURL - The URL to avatar image to display.
|
|
* @returns {void}
|
|
*/
|
|
UI.refreshAvatarDisplay = function(id) {
|
|
VideoLayout.changeUserAvatar(id);
|
|
};
|
|
|
|
/**
|
|
* Notify user that connection failed.
|
|
* @param {string} stropheErrorMsg raw Strophe error message
|
|
*/
|
|
UI.notifyConnectionFailed = function(stropheErrorMsg) {
|
|
let descriptionKey;
|
|
let descriptionArguments;
|
|
|
|
if (stropheErrorMsg) {
|
|
descriptionKey = 'dialog.connectErrorWithMsg';
|
|
descriptionArguments = { msg: stropheErrorMsg };
|
|
} else {
|
|
descriptionKey = 'dialog.connectError';
|
|
}
|
|
|
|
messageHandler.showError({
|
|
descriptionArguments,
|
|
descriptionKey,
|
|
titleKey: 'connection.CONNFAIL'
|
|
});
|
|
};
|
|
|
|
|
|
/**
|
|
* Notify user that maximum users limit has been reached.
|
|
*/
|
|
UI.notifyMaxUsersLimitReached = function() {
|
|
messageHandler.showError({
|
|
hideErrorSupportLink: true,
|
|
descriptionKey: 'dialog.maxUsersLimitReached',
|
|
titleKey: 'dialog.maxUsersLimitReachedTitle'
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Notify user that he was automatically muted when joned the conference.
|
|
*/
|
|
UI.notifyInitiallyMuted = function() {
|
|
messageHandler.participantNotification(
|
|
null,
|
|
'notify.mutedTitle',
|
|
'connected',
|
|
'notify.muted',
|
|
null);
|
|
};
|
|
|
|
UI.handleLastNEndpoints = function(leavingIds, enteringIds) {
|
|
VideoLayout.onLastNEndpointsChanged(leavingIds, enteringIds);
|
|
};
|
|
|
|
/**
|
|
* Update audio level visualization for specified user.
|
|
* @param {string} id user id
|
|
* @param {number} lvl audio level
|
|
*/
|
|
UI.setAudioLevel = (id, lvl) => VideoLayout.setAudioLevel(id, lvl);
|
|
|
|
UI.notifyTokenAuthFailed = function() {
|
|
messageHandler.showError({
|
|
descriptionKey: 'dialog.tokenAuthFailed',
|
|
titleKey: 'dialog.tokenAuthFailedTitle'
|
|
});
|
|
};
|
|
|
|
UI.notifyFocusDisconnected = function(focus, retrySec) {
|
|
messageHandler.participantNotification(
|
|
null, 'notify.focus',
|
|
'disconnected', 'notify.focusFail',
|
|
{ component: focus,
|
|
ms: retrySec }
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Update list of available physical devices.
|
|
*/
|
|
UI.onAvailableDevicesChanged = function() {
|
|
APP.conference.updateAudioIconEnabled();
|
|
APP.conference.updateVideoIconEnabled();
|
|
};
|
|
|
|
/**
|
|
* Returns the id of the current video shown on large.
|
|
* Currently used by tests (torture).
|
|
*/
|
|
UI.getLargeVideoID = function() {
|
|
return VideoLayout.getLargeVideoID();
|
|
};
|
|
|
|
/**
|
|
* Returns the current video shown on large.
|
|
* Currently used by tests (torture).
|
|
*/
|
|
UI.getLargeVideo = function() {
|
|
return VideoLayout.getLargeVideo();
|
|
};
|
|
|
|
/**
|
|
* Show shared video.
|
|
* @param {string} id the id of the sender of the command
|
|
* @param {string} url video url
|
|
* @param {string} attributes
|
|
*/
|
|
UI.onSharedVideoStart = function(id, url, attributes) {
|
|
if (sharedVideoManager) {
|
|
sharedVideoManager.onSharedVideoStart(id, url, attributes);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Update shared video.
|
|
* @param {string} id the id of the sender of the command
|
|
* @param {string} url video url
|
|
* @param {string} attributes
|
|
*/
|
|
UI.onSharedVideoUpdate = function(id, url, attributes) {
|
|
if (sharedVideoManager) {
|
|
sharedVideoManager.onSharedVideoUpdate(id, url, attributes);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Stop showing shared video.
|
|
* @param {string} id the id of the sender of the command
|
|
* @param {string} attributes
|
|
*/
|
|
UI.onSharedVideoStop = function(id, attributes) {
|
|
if (sharedVideoManager) {
|
|
sharedVideoManager.onSharedVideoStop(id, attributes);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Show shared video.
|
|
* @param {string} url video url
|
|
*/
|
|
UI.startSharedVideoEmitter = function(url) {
|
|
if (sharedVideoManager) {
|
|
sharedVideoManager.startSharedVideoEmitter(url);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Stop shared video.
|
|
*/
|
|
UI.stopSharedVideoEmitter = function() {
|
|
if (sharedVideoManager) {
|
|
sharedVideoManager.stopSharedVideoEmitter();
|
|
}
|
|
};
|
|
|
|
// TODO: Export every function separately. For now there is no point of doing
|
|
// this because we are importing everything.
|
|
export default UI;
|