You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. /* global APP, config */
  2. const UI = {};
  3. import Logger from '@jitsi/logger';
  4. import {
  5. conferenceWillInit
  6. } from '../../react/features/base/conference/actions';
  7. import { isMobileBrowser } from '../../react/features/base/environment/utils';
  8. import { setColorAlpha } from '../../react/features/base/util/helpers';
  9. import { sanitizeUrl } from '../../react/features/base/util/uri';
  10. import { setDocumentUrl } from '../../react/features/etherpad/actions';
  11. import {
  12. setNotificationsEnabled,
  13. showNotification
  14. } from '../../react/features/notifications/actions';
  15. import { NOTIFICATION_TIMEOUT_TYPE } from '../../react/features/notifications/constants';
  16. import { joinLeaveNotificationsDisabled } from '../../react/features/notifications/functions';
  17. import {
  18. dockToolbox,
  19. setToolboxEnabled,
  20. showToolbox
  21. } from '../../react/features/toolbox/actions.web';
  22. import EtherpadManager from './etherpad/Etherpad';
  23. import UIUtil from './util/UIUtil';
  24. import VideoLayout from './videolayout/VideoLayout';
  25. const logger = Logger.getLogger(__filename);
  26. let etherpadManager;
  27. /**
  28. * Indicates if we're currently in full screen mode.
  29. *
  30. * @return {boolean} {true} to indicate that we're currently in full screen
  31. * mode, {false} otherwise
  32. */
  33. UI.isFullScreen = function() {
  34. return UIUtil.isFullScreen();
  35. };
  36. /**
  37. * Initialize conference UI.
  38. */
  39. UI.initConference = function() {
  40. UI.showToolbar();
  41. };
  42. /**
  43. * Starts the UI module and initializes all related components.
  44. */
  45. UI.start = function() {
  46. APP.store.dispatch(conferenceWillInit());
  47. if (isMobileBrowser()) {
  48. document.body.classList.add('mobile-browser');
  49. } else {
  50. document.body.classList.add('desktop-browser');
  51. }
  52. if (config.backgroundAlpha !== undefined) {
  53. const backgroundColor = getComputedStyle(document.body).getPropertyValue('background-color');
  54. const alphaColor = setColorAlpha(backgroundColor, config.backgroundAlpha);
  55. document.body.style.backgroundColor = alphaColor;
  56. }
  57. if (config.iAmRecorder) {
  58. // in case of iAmSipGateway keep local video visible
  59. if (!config.iAmSipGateway) {
  60. APP.store.dispatch(setNotificationsEnabled(false));
  61. }
  62. APP.store.dispatch(setToolboxEnabled(false));
  63. }
  64. };
  65. /**
  66. * Handles etherpad click.
  67. */
  68. UI.onEtherpadClicked = function() {
  69. etherpadManager && etherpadManager.toggleEtherpad();
  70. };
  71. /**
  72. *
  73. */
  74. function onResize() {
  75. VideoLayout.onResize();
  76. }
  77. /**
  78. * Setup some DOM event listeners.
  79. */
  80. UI.bindEvents = () => {
  81. // Resize and reposition videos in full screen mode.
  82. document.addEventListener('webkitfullscreenchange', onResize);
  83. document.addEventListener('mozfullscreenchange', onResize);
  84. document.addEventListener('fullscreenchange', onResize);
  85. window.addEventListener('resize', onResize);
  86. };
  87. /**
  88. * Unbind some DOM event listeners.
  89. */
  90. UI.unbindEvents = () => {
  91. document.removeEventListener('webkitfullscreenchange', onResize);
  92. document.removeEventListener('mozfullscreenchange', onResize);
  93. document.removeEventListener('fullscreenchange', onResize);
  94. window.removeEventListener('resize', onResize);
  95. };
  96. /**
  97. * Setup and show Etherpad.
  98. * @param {string} name etherpad id
  99. */
  100. UI.initEtherpad = name => {
  101. const etherpadBaseUrl = sanitizeUrl(config.etherpad_base);
  102. if (etherpadManager || !etherpadBaseUrl || !name) {
  103. return;
  104. }
  105. logger.log('Etherpad is enabled');
  106. etherpadManager = new EtherpadManager();
  107. const url = new URL(name, etherpadBaseUrl);
  108. APP.store.dispatch(setDocumentUrl(url.toString()));
  109. if (config.openSharedDocumentOnJoin) {
  110. etherpadManager.toggleEtherpad();
  111. }
  112. };
  113. /**
  114. * Returns the shared document manager object.
  115. * @return {EtherpadManager} the shared document manager object
  116. */
  117. UI.getSharedDocumentManager = () => etherpadManager;
  118. /**
  119. * Show user on UI.
  120. * @param {JitsiParticipant} user
  121. */
  122. UI.addUser = function(user) {
  123. const status = user.getStatus();
  124. if (status) {
  125. // FIXME: move updateUserStatus in participantPresenceChanged action
  126. UI.updateUserStatus(user, status);
  127. }
  128. };
  129. /**
  130. * Updates the user status.
  131. *
  132. * @param {JitsiParticipant} user - The user which status we need to update.
  133. * @param {string} status - The new status.
  134. */
  135. UI.updateUserStatus = (user, status) => {
  136. const reduxState = APP.store.getState() || {};
  137. const { calleeInfoVisible } = reduxState['features/invite'] || {};
  138. // We hide status updates when join/leave notifications are disabled,
  139. // as jigasi is the component with statuses and they are seen as join/leave notifications.
  140. if (!status || calleeInfoVisible || joinLeaveNotificationsDisabled()) {
  141. return;
  142. }
  143. const displayName = user.getDisplayName();
  144. APP.store.dispatch(showNotification({
  145. titleKey: `${displayName} connected`,
  146. descriptionKey: 'dialOut.statusMessage'
  147. }, NOTIFICATION_TIMEOUT_TYPE.SHORT));
  148. };
  149. /**
  150. * Sets muted video state for participant
  151. */
  152. UI.setVideoMuted = function(id) {
  153. VideoLayout._updateLargeVideoIfDisplayed(id, true);
  154. if (APP.conference.isLocalId(id)) {
  155. APP.conference.updateVideoIconEnabled();
  156. }
  157. };
  158. UI.updateLargeVideo = (id, forceUpdate) => VideoLayout.updateLargeVideo(id, forceUpdate);
  159. // Used by torture.
  160. UI.showToolbar = timeout => APP.store.dispatch(showToolbox(timeout));
  161. // Used by torture.
  162. UI.dockToolbar = dock => APP.store.dispatch(dockToolbox(dock));
  163. UI.handleLastNEndpoints = function(leavingIds, enteringIds) {
  164. VideoLayout.onLastNEndpointsChanged(leavingIds, enteringIds);
  165. };
  166. /**
  167. * Update audio level visualization for specified user.
  168. * @param {string} id user id
  169. * @param {number} lvl audio level
  170. */
  171. UI.setAudioLevel = (id, lvl) => VideoLayout.setAudioLevel(id, lvl);
  172. /**
  173. * Returns the id of the current video shown on large.
  174. * Currently used by tests (torture).
  175. */
  176. UI.getLargeVideoID = function() {
  177. return VideoLayout.getLargeVideoID();
  178. };
  179. /**
  180. * Returns the current video shown on large.
  181. * Currently used by tests (torture).
  182. */
  183. UI.getLargeVideo = function() {
  184. return VideoLayout.getLargeVideo();
  185. };
  186. // TODO: Export every function separately. For now there is no point of doing
  187. // this because we are importing everything.
  188. export default UI;