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.

functions.web.ts 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. import { IReduxState } from '../app/types';
  2. import { hasAvailableDevices } from '../base/devices/functions';
  3. import { MEET_FEATURES } from '../base/jwt/constants';
  4. import { isJwtFeatureEnabled } from '../base/jwt/functions';
  5. import { IGUMPendingState } from '../base/media/types';
  6. import ChatButton from '../chat/components/web/ChatButton';
  7. import EmbedMeetingButton from '../embed-meeting/components/EmbedMeetingButton';
  8. import SharedDocumentButton from '../etherpad/components/SharedDocumentButton.web';
  9. import FeedbackButton from '../feedback/components/FeedbackButton.web';
  10. import InviteButton from '../invite/components/add-people-dialog/web/InviteButton';
  11. import KeyboardShortcutsButton from '../keyboard-shortcuts/components/web/KeyboardShortcutsButton';
  12. import NoiseSuppressionButton from '../noise-suppression/components/NoiseSuppressionButton';
  13. import ParticipantsPaneButton from '../participants-pane/components/web/ParticipantsPaneButton';
  14. import RaiseHandContainerButton from '../reactions/components/web/RaiseHandContainerButtons';
  15. import ReactionsMenuButton from '../reactions/components/web/ReactionsMenuButton';
  16. import LiveStreamButton from '../recording/components/LiveStream/web/LiveStreamButton';
  17. import RecordButton from '../recording/components/Recording/web/RecordButton';
  18. import ShareAudioButton from '../screen-share/components/web/ShareAudioButton';
  19. import { isScreenMediaShared } from '../screen-share/functions';
  20. import SecurityDialogButton from '../security/components/security-dialog/web/SecurityDialogButton';
  21. import SettingsButton from '../settings/components/web/SettingsButton';
  22. import SharedVideoButton from '../shared-video/components/web/SharedVideoButton';
  23. import SpeakerStatsButton from '../speaker-stats/components/web/SpeakerStatsButton';
  24. import ClosedCaptionButton from '../subtitles/components/web/ClosedCaptionButton';
  25. import TileViewButton from '../video-layout/components/TileViewButton';
  26. import VideoQualityButton from '../video-quality/components/VideoQualityButton.web';
  27. import VideoBackgroundButton from '../virtual-background/components/VideoBackgroundButton';
  28. import WhiteboardButton from '../whiteboard/components/web/WhiteboardButton';
  29. import { isWhiteboardVisible } from '../whiteboard/functions';
  30. import DownloadButton from './components/DownloadButton';
  31. import HelpButton from './components/HelpButton';
  32. import AudioSettingsButton from './components/web/AudioSettingsButton';
  33. import CustomOptionButton from './components/web/CustomOptionButton';
  34. import FullscreenButton from './components/web/FullscreenButton';
  35. import LinkToSalesforceButton from './components/web/LinkToSalesforceButton';
  36. import ProfileButton from './components/web/ProfileButton';
  37. import ShareDesktopButton from './components/web/ShareDesktopButton';
  38. import ToggleCameraButton from './components/web/ToggleCameraButton';
  39. import VideoSettingsButton from './components/web/VideoSettingsButton';
  40. import { TOOLBAR_TIMEOUT } from './constants';
  41. import { IToolboxButton, NOTIFY_CLICK_MODE } from './types';
  42. export * from './functions.any';
  43. /**
  44. * Helper for getting the height of the toolbox.
  45. *
  46. * @returns {number} The height of the toolbox.
  47. */
  48. export function getToolboxHeight() {
  49. const toolbox = document.getElementById('new-toolbox');
  50. return toolbox?.clientHeight || 0;
  51. }
  52. /**
  53. * Checks if the specified button is enabled.
  54. *
  55. * @param {string} buttonName - The name of the button. See {@link interfaceConfig}.
  56. * @param {Object|Array<string>} state - The redux state or the array with the enabled buttons.
  57. * @returns {boolean} - True if the button is enabled and false otherwise.
  58. */
  59. export function isButtonEnabled(buttonName: string, state: IReduxState | Array<string>) {
  60. const buttons = Array.isArray(state) ? state : state['features/toolbox'].toolbarButtons || [];
  61. return buttons.includes(buttonName);
  62. }
  63. /**
  64. * Indicates if the toolbox is visible or not.
  65. *
  66. * @param {IReduxState} state - The state from the Redux store.
  67. * @returns {boolean} - True to indicate that the toolbox is visible, false -
  68. * otherwise.
  69. */
  70. export function isToolboxVisible(state: IReduxState) {
  71. const { iAmRecorder, iAmSipGateway, toolbarConfig } = state['features/base/config'];
  72. const { alwaysVisible } = toolbarConfig || {};
  73. const {
  74. timeoutID,
  75. visible
  76. } = state['features/toolbox'];
  77. const { audioSettingsVisible, videoSettingsVisible } = state['features/settings'];
  78. const whiteboardVisible = isWhiteboardVisible(state);
  79. return Boolean(!iAmRecorder && !iAmSipGateway
  80. && (
  81. timeoutID
  82. || visible
  83. || alwaysVisible
  84. || audioSettingsVisible
  85. || videoSettingsVisible
  86. || whiteboardVisible
  87. ));
  88. }
  89. /**
  90. * Indicates if the audio settings button is disabled or not.
  91. *
  92. * @param {IReduxState} state - The state from the Redux store.
  93. * @returns {boolean}
  94. */
  95. export function isAudioSettingsButtonDisabled(state: IReduxState) {
  96. return !(hasAvailableDevices(state, 'audioInput')
  97. || hasAvailableDevices(state, 'audioOutput'))
  98. || state['features/base/config'].startSilent;
  99. }
  100. /**
  101. * Indicates if the desktop share button is disabled or not.
  102. *
  103. * @param {IReduxState} state - The state from the Redux store.
  104. * @returns {boolean}
  105. */
  106. export function isDesktopShareButtonDisabled(state: IReduxState) {
  107. const { muted, unmuteBlocked } = state['features/base/media'].video;
  108. const videoOrShareInProgress = !muted || isScreenMediaShared(state);
  109. const enabledInJwt = isJwtFeatureEnabled(state, MEET_FEATURES.SCREEN_SHARING, true, true);
  110. return !enabledInJwt || (unmuteBlocked && !videoOrShareInProgress);
  111. }
  112. /**
  113. * Indicates if the video settings button is disabled or not.
  114. *
  115. * @param {IReduxState} state - The state from the Redux store.
  116. * @returns {boolean}
  117. */
  118. export function isVideoSettingsButtonDisabled(state: IReduxState) {
  119. return !hasAvailableDevices(state, 'videoInput');
  120. }
  121. /**
  122. * Indicates if the video mute button is disabled or not.
  123. *
  124. * @param {IReduxState} state - The state from the Redux store.
  125. * @returns {boolean}
  126. */
  127. export function isVideoMuteButtonDisabled(state: IReduxState) {
  128. const { muted, unmuteBlocked, gumPending } = state['features/base/media'].video;
  129. return !hasAvailableDevices(state, 'videoInput')
  130. || (unmuteBlocked && Boolean(muted))
  131. || gumPending !== IGUMPendingState.NONE;
  132. }
  133. /**
  134. * If an overflow drawer should be displayed or not.
  135. * This is usually done for mobile devices or on narrow screens.
  136. *
  137. * @param {IReduxState} state - The state from the Redux store.
  138. * @returns {boolean}
  139. */
  140. export function showOverflowDrawer(state: IReduxState) {
  141. return state['features/toolbox'].overflowDrawer;
  142. }
  143. /**
  144. * Returns true if the overflow menu button is displayed and false otherwise.
  145. *
  146. * @param {IReduxState} state - The state from the Redux store.
  147. * @returns {boolean} - True if the overflow menu button is displayed and false otherwise.
  148. */
  149. export function showOverflowMenu(state: IReduxState) {
  150. return state['features/toolbox'].overflowMenuVisible;
  151. }
  152. /**
  153. * Indicates whether the toolbox is enabled or not.
  154. *
  155. * @param {IReduxState} state - The state from the Redux store.
  156. * @returns {boolean}
  157. */
  158. export function isToolboxEnabled(state: IReduxState) {
  159. return state['features/toolbox'].enabled;
  160. }
  161. /**
  162. * Returns the toolbar timeout from config or the default value.
  163. *
  164. * @param {IReduxState} state - The state from the Redux store.
  165. * @returns {number} - Toolbar timeout in milliseconds.
  166. */
  167. export function getToolbarTimeout(state: IReduxState) {
  168. const { toolbarConfig } = state['features/base/config'];
  169. return toolbarConfig?.timeout || TOOLBAR_TIMEOUT;
  170. }
  171. /**
  172. * Returns all buttons that could be rendered.
  173. *
  174. * @param {Object} _customToolbarButtons - An array containing custom buttons objects.
  175. * @returns {Object} The button maps mainMenuButtons and overflowMenuButtons.
  176. */
  177. export function getAllToolboxButtons(_customToolbarButtons?: {
  178. backgroundColor?: string;
  179. icon: string;
  180. id: string;
  181. text: string;
  182. }[]): { [key: string]: IToolboxButton; } {
  183. const microphone = {
  184. key: 'microphone',
  185. Content: AudioSettingsButton,
  186. group: 0
  187. };
  188. const camera = {
  189. key: 'camera',
  190. Content: VideoSettingsButton,
  191. group: 0
  192. };
  193. const profile = {
  194. key: 'profile',
  195. Content: ProfileButton,
  196. group: 1
  197. };
  198. const chat = {
  199. key: 'chat',
  200. Content: ChatButton,
  201. group: 2
  202. };
  203. const desktop = {
  204. key: 'desktop',
  205. Content: ShareDesktopButton,
  206. group: 2
  207. };
  208. // In Narrow layout and mobile web we are using drawer for popups and that is why it is better to include
  209. // all forms of reactions in the overflow menu. Otherwise the toolbox will be hidden and the reactions popup
  210. // misaligned.
  211. const raisehand = {
  212. key: 'raisehand',
  213. Content: RaiseHandContainerButton,
  214. group: 2
  215. };
  216. const reactions = {
  217. key: 'reactions',
  218. Content: ReactionsMenuButton,
  219. group: 2
  220. };
  221. const participants = {
  222. key: 'participants-pane',
  223. Content: ParticipantsPaneButton,
  224. group: 2
  225. };
  226. const invite = {
  227. key: 'invite',
  228. Content: InviteButton,
  229. group: 2
  230. };
  231. const tileview = {
  232. key: 'tileview',
  233. Content: TileViewButton,
  234. group: 2
  235. };
  236. const toggleCamera = {
  237. key: 'toggle-camera',
  238. Content: ToggleCameraButton,
  239. group: 2
  240. };
  241. const videoQuality = {
  242. key: 'videoquality',
  243. Content: VideoQualityButton,
  244. group: 2
  245. };
  246. const fullscreen = {
  247. key: 'fullscreen',
  248. Content: FullscreenButton,
  249. group: 2
  250. };
  251. const security = {
  252. key: 'security',
  253. alias: 'info',
  254. Content: SecurityDialogButton,
  255. group: 2
  256. };
  257. const cc = {
  258. key: 'closedcaptions',
  259. Content: ClosedCaptionButton,
  260. group: 2
  261. };
  262. const recording = {
  263. key: 'recording',
  264. Content: RecordButton,
  265. group: 2
  266. };
  267. const livestreaming = {
  268. key: 'livestreaming',
  269. Content: LiveStreamButton,
  270. group: 2
  271. };
  272. const linkToSalesforce = {
  273. key: 'linktosalesforce',
  274. Content: LinkToSalesforceButton,
  275. group: 2
  276. };
  277. const shareVideo = {
  278. key: 'sharedvideo',
  279. Content: SharedVideoButton,
  280. group: 3
  281. };
  282. const shareAudio = {
  283. key: 'shareaudio',
  284. Content: ShareAudioButton,
  285. group: 3
  286. };
  287. const noiseSuppression = {
  288. key: 'noisesuppression',
  289. Content: NoiseSuppressionButton,
  290. group: 3
  291. };
  292. const whiteboard = {
  293. key: 'whiteboard',
  294. Content: WhiteboardButton,
  295. group: 3
  296. };
  297. const etherpad = {
  298. key: 'etherpad',
  299. Content: SharedDocumentButton,
  300. group: 3
  301. };
  302. const virtualBackground = {
  303. key: 'select-background',
  304. Content: VideoBackgroundButton,
  305. group: 3
  306. };
  307. const speakerStats = {
  308. key: 'stats',
  309. Content: SpeakerStatsButton,
  310. group: 3
  311. };
  312. const settings = {
  313. key: 'settings',
  314. Content: SettingsButton,
  315. group: 4
  316. };
  317. const shortcuts = {
  318. key: 'shortcuts',
  319. Content: KeyboardShortcutsButton,
  320. group: 4
  321. };
  322. const embed = {
  323. key: 'embedmeeting',
  324. Content: EmbedMeetingButton,
  325. group: 4
  326. };
  327. const feedback = {
  328. key: 'feedback',
  329. Content: FeedbackButton,
  330. group: 4
  331. };
  332. const download = {
  333. key: 'download',
  334. Content: DownloadButton,
  335. group: 4
  336. };
  337. const help = {
  338. key: 'help',
  339. Content: HelpButton,
  340. group: 4
  341. };
  342. const customButtons = _customToolbarButtons?.reduce((prev, { backgroundColor, icon, id, text }) => {
  343. return {
  344. ...prev,
  345. [id]: {
  346. backgroundColor,
  347. key: id,
  348. Content: CustomOptionButton,
  349. group: 4,
  350. icon,
  351. text
  352. }
  353. };
  354. }, {});
  355. return {
  356. microphone,
  357. camera,
  358. profile,
  359. desktop,
  360. chat,
  361. raisehand,
  362. reactions,
  363. participants,
  364. invite,
  365. tileview,
  366. toggleCamera,
  367. videoQuality,
  368. fullscreen,
  369. security,
  370. cc,
  371. recording,
  372. livestreaming,
  373. linkToSalesforce,
  374. shareVideo,
  375. shareAudio,
  376. noiseSuppression,
  377. whiteboard,
  378. etherpad,
  379. virtualBackground,
  380. speakerStats,
  381. settings,
  382. shortcuts,
  383. embed,
  384. feedback,
  385. download,
  386. help,
  387. ...customButtons
  388. };
  389. }
  390. /**
  391. * Returns the list of participant menu buttons that have that notify the api when clicked.
  392. *
  393. * @param {Object} state - The redux state.
  394. * @returns {Map<string, NOTIFY_CLICK_MODE>} - The list of participant menu buttons.
  395. */
  396. export function getParticipantMenuButtonsWithNotifyClick(state: IReduxState): Map<string, NOTIFY_CLICK_MODE> {
  397. return state['features/toolbox'].participantMenuButtonsWithNotifyClick;
  398. }