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.

defaultToolbarButtons.web.js 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. // @flow
  2. import React from 'react';
  3. import {
  4. ACTION_SHORTCUT_TRIGGERED as TRIGGERED,
  5. AUDIO_MUTE,
  6. VIDEO_MUTE,
  7. createShortcutEvent,
  8. createToolbarEvent,
  9. sendAnalytics
  10. } from '../analytics';
  11. import { ParticipantCounter } from '../contact-list';
  12. import { openDeviceSelectionDialog } from '../device-selection';
  13. import { InfoDialogButton, openInviteDialog } from '../invite';
  14. import UIEvents from '../../../service/UI/UIEvents';
  15. import { VideoQualityButton } from '../video-quality';
  16. import ProfileButton from './components/ProfileButton';
  17. declare var APP: Object;
  18. declare var interfaceConfig: Object;
  19. /**
  20. * The cache of {@link getDefaultButtons()}.
  21. */
  22. let defaultButtons: Object;
  23. /**
  24. * Returns a map of all button descriptors and according properties.
  25. *
  26. * @returns {Object} - The maps of default button descriptors.
  27. */
  28. export default function getDefaultButtons() {
  29. if (defaultButtons) {
  30. return defaultButtons;
  31. }
  32. defaultButtons = {
  33. /**
  34. * The descriptor of the camera toolbar button.
  35. */
  36. camera: {
  37. classNames: [ 'button', 'icon-camera' ],
  38. enabled: true,
  39. isDisplayed: () => true,
  40. id: 'toolbar_button_camera',
  41. onClick() {
  42. // TODO: Why is this different from the code which handles
  43. // a keyboard shortcut?
  44. const newVideoMutedState = !APP.conference.isLocalVideoMuted();
  45. // The 'enable' attribute in the event is set to true if the
  46. // button click triggered a mute action, and set to false if it
  47. // triggered an unmute action.
  48. sendAnalytics(createToolbarEvent(
  49. VIDEO_MUTE,
  50. {
  51. enable: newVideoMutedState
  52. }));
  53. APP.UI.emitEvent(UIEvents.VIDEO_MUTED, newVideoMutedState);
  54. },
  55. popups: [
  56. {
  57. dataAttr: 'audioOnly.featureToggleDisabled',
  58. dataInterpolate: { feature: 'video mute' },
  59. id: 'unmuteWhileAudioOnly'
  60. }
  61. ],
  62. shortcut: 'V',
  63. shortcutAttr: 'toggleVideoPopover',
  64. shortcutFunc() {
  65. if (APP.conference.isAudioOnly()) {
  66. APP.UI.emitEvent(UIEvents.VIDEO_UNMUTING_WHILE_AUDIO_ONLY);
  67. return;
  68. }
  69. // The 'enable' attribute in the event is set to true if the
  70. // shortcut triggered a mute action, and set to false if it
  71. // triggered an unmute action.
  72. sendAnalytics(createShortcutEvent(
  73. VIDEO_MUTE,
  74. TRIGGERED,
  75. { enable: !APP.conference.isLocalVideoMuted() }));
  76. APP.conference.toggleVideoMuted();
  77. },
  78. shortcutDescription: 'keyboardShortcuts.videoMute',
  79. tooltipKey: 'toolbar.videomute'
  80. },
  81. /**
  82. * The descriptor of the chat toolbar button.
  83. */
  84. chat: {
  85. classNames: [ 'button', 'icon-chat' ],
  86. enabled: true,
  87. html: <span className = 'badge-round'>
  88. <span id = 'unreadMessages' /></span>,
  89. id: 'toolbar_button_chat',
  90. onClick() {
  91. // The 'enable' attribute is set to true if the click resulted
  92. // in the chat panel being shown, and to false if it was hidden.
  93. sendAnalytics(createToolbarEvent(
  94. 'toggle.chat',
  95. {
  96. enable: !APP.UI.Chat.isVisible()
  97. }));
  98. APP.UI.emitEvent(UIEvents.TOGGLE_CHAT);
  99. },
  100. shortcut: 'C',
  101. shortcutAttr: 'toggleChatPopover',
  102. shortcutFunc() {
  103. // The 'enable' attribute is set to true if the shortcut
  104. // resulted in the chat panel being shown, and to false if it
  105. // was hidden.
  106. sendAnalytics(createShortcutEvent(
  107. 'toggle.chat',
  108. {
  109. enable: !APP.UI.Chat.isVisible()
  110. }));
  111. APP.UI.toggleChat();
  112. },
  113. shortcutDescription: 'keyboardShortcuts.toggleChat',
  114. sideContainerId: 'chat_container',
  115. tooltipKey: 'toolbar.chat'
  116. },
  117. /**
  118. * The descriptor of the contact list toolbar button.
  119. */
  120. contacts: {
  121. childComponent: ParticipantCounter,
  122. classNames: [ 'button', 'icon-contactList' ],
  123. enabled: true,
  124. id: 'toolbar_contact_list',
  125. onClick() {
  126. // TODO: Include an 'enable' attribute which specifies whether
  127. // the contacts panel was shown or hidden.
  128. sendAnalytics(createToolbarEvent('contacts'));
  129. APP.UI.emitEvent(UIEvents.TOGGLE_CONTACT_LIST);
  130. },
  131. sideContainerId: 'contacts_container',
  132. tooltipKey: 'bottomtoolbar.contactlist'
  133. },
  134. /**
  135. * The descriptor of the desktop sharing toolbar button.
  136. */
  137. desktop: {
  138. classNames: [ 'button', 'icon-share-desktop' ],
  139. enabled: true,
  140. id: 'toolbar_button_desktopsharing',
  141. onClick() {
  142. // TODO: Why is the button clicked handled differently that
  143. // a keyboard shortcut press (firing a TOGGLE_SCREENSHARING
  144. // event vs. directly calling toggleScreenSharing())?
  145. sendAnalytics(createToolbarEvent(
  146. 'screen.sharing',
  147. {
  148. enable: !APP.conference.isSharingScreen
  149. }));
  150. APP.UI.emitEvent(UIEvents.TOGGLE_SCREENSHARING);
  151. },
  152. popups: [
  153. {
  154. dataAttr: 'audioOnly.featureToggleDisabled',
  155. dataInterpolate: { feature: 'screen sharing' },
  156. id: 'screenshareWhileAudioOnly'
  157. }
  158. ],
  159. shortcut: 'D',
  160. shortcutAttr: 'toggleDesktopSharingPopover',
  161. shortcutFunc() {
  162. // The 'enable' attribute is set to true if pressing the
  163. // shortcut resulted in screen sharing being enabled, and false
  164. // if it resulted in screen sharing being disabled.
  165. sendAnalytics(createShortcutEvent(
  166. 'toggle.screen.sharing',
  167. TRIGGERED,
  168. { enable: !APP.conference.isSharingScreen }));
  169. // eslint-disable-next-line no-empty-function
  170. APP.conference.toggleScreenSharing().catch(() => {});
  171. },
  172. shortcutDescription: 'keyboardShortcuts.toggleScreensharing',
  173. tooltipKey: 'toolbar.sharescreen'
  174. },
  175. /**
  176. * The descriptor of the device selection toolbar button.
  177. */
  178. fodeviceselection: {
  179. classNames: [ 'button', 'icon-settings' ],
  180. enabled: true,
  181. isDisplayed() {
  182. return interfaceConfig.filmStripOnly;
  183. },
  184. id: 'toolbar_button_fodeviceselection',
  185. onClick(dispatch: Function) {
  186. sendAnalytics(
  187. createToolbarEvent('filmstrip.only.device.selection'));
  188. dispatch(openDeviceSelectionDialog());
  189. },
  190. sideContainerId: 'settings_container',
  191. tooltipKey: 'toolbar.Settings'
  192. },
  193. /**
  194. * The descriptor of the dialpad toolbar button.
  195. */
  196. dialpad: {
  197. classNames: [ 'button', 'icon-dialpad' ],
  198. enabled: true,
  199. // TODO: remove it after UI.updateDTMFSupport fix
  200. hidden: true,
  201. id: 'toolbar_button_dialpad',
  202. onClick() {
  203. sendAnalytics(createToolbarEvent('dialpad'));
  204. },
  205. tooltipKey: 'toolbar.dialpad'
  206. },
  207. /**
  208. * The descriptor of the etherpad toolbar button.
  209. */
  210. etherpad: {
  211. classNames: [ 'button', 'icon-share-doc' ],
  212. enabled: true,
  213. hidden: true,
  214. id: 'toolbar_button_etherpad',
  215. onClick() {
  216. // The 'enable' attribute is set to true if the click resulted
  217. // in the etherpad panel being shown, or false it it was hidden.
  218. sendAnalytics(createToolbarEvent(
  219. 'toggle.etherpad',
  220. {
  221. enable: !APP.UI.isEtherpadVisible()
  222. }));
  223. APP.UI.emitEvent(UIEvents.ETHERPAD_CLICKED);
  224. },
  225. tooltipKey: 'toolbar.etherpad'
  226. },
  227. /**
  228. * The descriptor of the toolbar button which toggles full-screen mode.
  229. */
  230. fullscreen: {
  231. classNames: [ 'button', 'icon-full-screen' ],
  232. enabled: true,
  233. id: 'toolbar_button_fullScreen',
  234. onClick() {
  235. // TODO: why is the fullscreen button handled differently than
  236. // the fullscreen keyboard shortcut (one results in a direct
  237. // call to toggleFullScreen, while the other fires an
  238. // UIEvents.TOGGLE_FULLSCREEN event)?
  239. // The 'enable' attribute is set to true if the action resulted
  240. // in fullscreen mode being enabled.
  241. sendAnalytics(createToolbarEvent(
  242. 'toggle.fullscreen',
  243. {
  244. enable: !APP.UI.isFullScreen()
  245. }));
  246. APP.UI.emitEvent(UIEvents.TOGGLE_FULLSCREEN);
  247. },
  248. shortcut: 'S',
  249. shortcutAttr: 'toggleFullscreenPopover',
  250. shortcutDescription: 'keyboardShortcuts.fullScreen',
  251. shortcutFunc() {
  252. // The 'enable' attribute is set to true if the action resulted
  253. // in fullscreen mode being enabled.
  254. sendAnalytics(createShortcutEvent(
  255. 'toggle.fullscreen',
  256. {
  257. enable: !APP.UI.isFullScreen()
  258. }));
  259. APP.UI.toggleFullScreen();
  260. },
  261. tooltipKey: 'toolbar.fullscreen'
  262. },
  263. /**
  264. * The descriptor of the toolbar button which hangs up the
  265. * call/conference.
  266. */
  267. hangup: {
  268. classNames: [ 'button', 'icon-hangup', 'button_hangup' ],
  269. enabled: true,
  270. isDisplayed: () => true,
  271. id: 'toolbar_button_hangup',
  272. onClick() {
  273. sendAnalytics(createToolbarEvent('hangup'));
  274. APP.UI.emitEvent(UIEvents.HANGUP);
  275. },
  276. tooltipKey: 'toolbar.hangup'
  277. },
  278. /**
  279. * The descriptor of the toolbar button which opens a dialog for the
  280. * conference URL and inviting others.
  281. */
  282. info: {
  283. component: InfoDialogButton
  284. },
  285. /**
  286. * The descriptor of the toolbar button which shows the invite user
  287. * dialog.
  288. */
  289. invite: {
  290. classNames: [ 'button', 'icon-link' ],
  291. enabled: true,
  292. id: 'toolbar_button_link',
  293. onClick(dispatch: Function) {
  294. sendAnalytics(createToolbarEvent('invite'));
  295. dispatch(openInviteDialog());
  296. },
  297. tooltipKey: 'toolbar.invite'
  298. },
  299. /**
  300. * The descriptor of the microphone toolbar button.
  301. */
  302. microphone: {
  303. classNames: [ 'button', 'icon-microphone' ],
  304. enabled: true,
  305. isDisplayed: () => true,
  306. id: 'toolbar_button_mute',
  307. onClick() {
  308. const sharedVideoManager = APP.UI.getSharedVideoManager();
  309. // TODO: Clicking the mute button and pressing the mute shortcut
  310. // could be handled in a uniform manner. The code below checks
  311. // the mute status and fires the appropriate event (MUTED or
  312. // UNMUTED), while the code which handles the keyboard shortcut
  313. // calls toggleAudioMuted(). Also strangely the the user is
  314. // only warned if they click the button (and not if they use
  315. // the shortcut).
  316. if (APP.conference.isLocalAudioMuted()) {
  317. // If there's a shared video with the volume "on" and we
  318. // aren't the video owner, we warn the user
  319. // that currently it's not possible to unmute.
  320. if (sharedVideoManager
  321. && sharedVideoManager.isSharedVideoVolumeOn()
  322. && !sharedVideoManager.isSharedVideoOwner()) {
  323. APP.UI.showCustomToolbarPopup(
  324. 'microphone', 'unableToUnmutePopup', true, 5000);
  325. } else {
  326. sendAnalytics(createToolbarEvent(
  327. AUDIO_MUTE,
  328. { enable: false }));
  329. APP.UI.emitEvent(UIEvents.AUDIO_MUTED, false, true);
  330. }
  331. } else {
  332. sendAnalytics(createToolbarEvent(
  333. AUDIO_MUTE,
  334. { enable: true }));
  335. APP.UI.emitEvent(UIEvents.AUDIO_MUTED, true, true);
  336. }
  337. },
  338. popups: [
  339. {
  340. dataAttr: 'toolbar.micMutedPopup',
  341. id: 'micMutedPopup'
  342. },
  343. {
  344. dataAttr: 'toolbar.unableToUnmutePopup',
  345. id: 'unableToUnmutePopup'
  346. },
  347. {
  348. dataAttr: 'toolbar.talkWhileMutedPopup',
  349. id: 'talkWhileMutedPopup'
  350. }
  351. ],
  352. shortcut: 'M',
  353. shortcutAttr: 'mutePopover',
  354. shortcutFunc() {
  355. // The 'enable' attribute in the event is set to true if the
  356. // shortcut triggered a mute action, and set to false if it
  357. // triggered an unmute action.
  358. sendAnalytics(createShortcutEvent(
  359. AUDIO_MUTE,
  360. TRIGGERED,
  361. { enable: !APP.conference.isLocalAudioMuted() }));
  362. APP.conference.toggleAudioMuted();
  363. },
  364. shortcutDescription: 'keyboardShortcuts.mute',
  365. tooltipKey: 'toolbar.mute'
  366. },
  367. /**
  368. * The descriptor of the profile toolbar button.
  369. */
  370. profile: {
  371. component: ProfileButton,
  372. sideContainerId: 'profile_container'
  373. },
  374. /**
  375. * The descriptor of the "Raise hand" toolbar button.
  376. */
  377. raisehand: {
  378. classNames: [ 'button', 'icon-raised-hand' ],
  379. enabled: true,
  380. id: 'toolbar_button_raisehand',
  381. onClick() {
  382. // TODO: reduce duplication with shortcutFunc below.
  383. // The 'enable' attribute is set to true if the pressing of the
  384. // shortcut resulted in the hand being raised, and to false
  385. // if it resulted in the hand being 'lowered'.
  386. sendAnalytics(createToolbarEvent(
  387. 'raise.hand',
  388. { enable: !APP.conference.isHandRaised }));
  389. APP.conference.maybeToggleRaisedHand();
  390. },
  391. shortcut: 'R',
  392. shortcutAttr: 'raiseHandPopover',
  393. shortcutDescription: 'keyboardShortcuts.raiseHand',
  394. shortcutFunc() {
  395. // The 'enable' attribute is set to true if the pressing of the
  396. // shortcut resulted in the hand being raised, and to false
  397. // if it resulted in the hand being 'lowered'.
  398. sendAnalytics(createShortcutEvent(
  399. 'toggle.raise.hand',
  400. TRIGGERED,
  401. { enable: !APP.conference.isHandRaised }));
  402. APP.conference.maybeToggleRaisedHand();
  403. },
  404. tooltipKey: 'toolbar.raiseHand'
  405. },
  406. /**
  407. * The descriptor of the recording toolbar button. Requires additional
  408. * initialization in the recording module.
  409. */
  410. recording: {
  411. classNames: [ 'button' ],
  412. enabled: true,
  413. // will be displayed once the recording functionality is detected
  414. hidden: true,
  415. id: 'toolbar_button_record',
  416. tooltipKey: 'liveStreaming.buttonTooltip'
  417. },
  418. /**
  419. * The descriptor of the settings toolbar button.
  420. */
  421. settings: {
  422. classNames: [ 'button', 'icon-settings' ],
  423. enabled: true,
  424. id: 'toolbar_button_settings',
  425. onClick() {
  426. // TODO: Include an 'enable' attribute which specifies whether
  427. // the settings panel was shown or hidden.
  428. sendAnalytics(createToolbarEvent('settings'));
  429. APP.UI.emitEvent(UIEvents.TOGGLE_SETTINGS);
  430. },
  431. sideContainerId: 'settings_container',
  432. tooltipKey: 'toolbar.Settings'
  433. },
  434. /**
  435. * The descriptor of the "Share YouTube video" toolbar button.
  436. */
  437. sharedvideo: {
  438. classNames: [ 'button', 'icon-shared-video' ],
  439. enabled: true,
  440. id: 'toolbar_button_sharedvideo',
  441. onClick() {
  442. // The 'enable' attribute is set to true if the click resulted
  443. // in the "start sharing video" dialog being shown, and false
  444. // if it resulted in the "stop sharing video" dialog being
  445. // shown.
  446. sendAnalytics(createToolbarEvent(
  447. 'shared.video.toggled',
  448. {
  449. enable: !APP.UI.isSharedVideoShown()
  450. }));
  451. APP.UI.emitEvent(UIEvents.SHARED_VIDEO_CLICKED);
  452. },
  453. popups: [
  454. {
  455. dataAttr: 'toolbar.sharedVideoMutedPopup',
  456. id: 'sharedVideoMutedPopup'
  457. }
  458. ],
  459. tooltipKey: 'toolbar.sharedvideo'
  460. },
  461. videoquality: {
  462. component: VideoQualityButton
  463. }
  464. };
  465. Object.keys(defaultButtons).forEach(name => {
  466. const button = defaultButtons[name];
  467. if (!button.isDisplayed) {
  468. button.isDisplayed = _isDisplayed;
  469. }
  470. });
  471. return defaultButtons;
  472. }
  473. /**
  474. * The default implementation of the {@code isDisplayed} method of the toolbar
  475. * button definition returned by {@link getDefaultButtons()}.
  476. *
  477. * @returns {boolean} If the user intarface is full i.e. not filmstrip-only,
  478. * then {@code true}; otherwise, {@code false}.
  479. */
  480. function _isDisplayed() {
  481. return !interfaceConfig.filmStripOnly;
  482. }