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.

API.js 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857
  1. // @flow
  2. import * as JitsiMeetConferenceEvents from '../../ConferenceEvents';
  3. import {
  4. createApiEvent,
  5. sendAnalytics
  6. } from '../../react/features/analytics';
  7. import {
  8. sendTones,
  9. setPassword,
  10. setSubject
  11. } from '../../react/features/base/conference';
  12. import { parseJWTFromURLParams } from '../../react/features/base/jwt';
  13. import { setE2EEKey } from '../../react/features/e2ee';
  14. import { invite } from '../../react/features/invite';
  15. import { toggleTileView } from '../../react/features/video-layout';
  16. import { setVideoQuality } from '../../react/features/video-quality';
  17. import { getJitsiMeetTransport } from '../transport';
  18. import { muteAllParticipants } from '../../react/features/remote-video-menu/actions';
  19. import { API_ID, ENDPOINT_TEXT_MESSAGE_NAME } from './constants';
  20. import {
  21. processExternalDeviceRequest
  22. } from '../../react/features/device-selection/functions';
  23. const logger = require('jitsi-meet-logger').getLogger(__filename);
  24. declare var APP: Object;
  25. /**
  26. * List of the available commands.
  27. */
  28. let commands = {};
  29. /**
  30. * The state of screen sharing(started/stopped) before the screen sharing is
  31. * enabled and initialized.
  32. * NOTE: This flag help us to cache the state and use it if toggle-share-screen
  33. * was received before the initialization.
  34. */
  35. let initialScreenSharingState = false;
  36. /**
  37. * The transport instance used for communication with external apps.
  38. *
  39. * @type {Transport}
  40. */
  41. const transport = getJitsiMeetTransport();
  42. /**
  43. * The current audio availability.
  44. *
  45. * @type {boolean}
  46. */
  47. let audioAvailable = true;
  48. /**
  49. * The current video availability.
  50. *
  51. * @type {boolean}
  52. */
  53. let videoAvailable = true;
  54. /**
  55. * Initializes supported commands.
  56. *
  57. * @returns {void}
  58. */
  59. function initCommands() {
  60. commands = {
  61. 'display-name': displayName => {
  62. sendAnalytics(createApiEvent('display.name.changed'));
  63. APP.conference.changeLocalDisplayName(displayName);
  64. },
  65. 'mute-everyone': () => {
  66. sendAnalytics(createApiEvent('muted-everyone'));
  67. const participants = APP.store.getState()['features/base/participants'];
  68. const localIds = participants
  69. .filter(participant => participant.local)
  70. .filter(participant => participant.role === 'moderator')
  71. .map(participant => participant.id);
  72. APP.store.dispatch(muteAllParticipants(localIds));
  73. },
  74. 'password': password => {
  75. const { conference, passwordRequired }
  76. = APP.store.getState()['features/base/conference'];
  77. if (passwordRequired) {
  78. sendAnalytics(createApiEvent('submit.password'));
  79. APP.store.dispatch(setPassword(
  80. passwordRequired,
  81. passwordRequired.join,
  82. password
  83. ));
  84. } else {
  85. sendAnalytics(createApiEvent('password.changed'));
  86. APP.store.dispatch(setPassword(
  87. conference,
  88. conference.lock,
  89. password
  90. ));
  91. }
  92. },
  93. 'proxy-connection-event': event => {
  94. APP.conference.onProxyConnectionEvent(event);
  95. },
  96. 'send-tones': (options = {}) => {
  97. const { duration, tones, pause } = options;
  98. APP.store.dispatch(sendTones(tones, duration, pause));
  99. },
  100. 'subject': subject => {
  101. sendAnalytics(createApiEvent('subject.changed'));
  102. APP.store.dispatch(setSubject(subject));
  103. },
  104. 'submit-feedback': feedback => {
  105. sendAnalytics(createApiEvent('submit.feedback'));
  106. APP.conference.submitFeedback(feedback.score, feedback.message);
  107. },
  108. 'toggle-audio': () => {
  109. sendAnalytics(createApiEvent('toggle-audio'));
  110. logger.log('Audio toggle: API command received');
  111. APP.conference.toggleAudioMuted(false /* no UI */);
  112. },
  113. 'toggle-video': () => {
  114. sendAnalytics(createApiEvent('toggle-video'));
  115. logger.log('Video toggle: API command received');
  116. APP.conference.toggleVideoMuted(false /* no UI */);
  117. },
  118. 'toggle-film-strip': () => {
  119. sendAnalytics(createApiEvent('film.strip.toggled'));
  120. APP.UI.toggleFilmstrip();
  121. },
  122. 'toggle-chat': () => {
  123. sendAnalytics(createApiEvent('chat.toggled'));
  124. APP.UI.toggleChat();
  125. },
  126. /**
  127. * Callback to invoke when the "toggle-share-screen" command is received.
  128. *
  129. * @param {Object} options - Additional details of how to perform
  130. * the action. Note this parameter is undocumented and experimental.
  131. * @param {boolean} options.enable - Whether trying to enable screen
  132. * sharing or to turn it off.
  133. * @returns {void}
  134. */
  135. 'toggle-share-screen': (options = {}) => {
  136. sendAnalytics(createApiEvent('screen.sharing.toggled'));
  137. toggleScreenSharing(options.enable);
  138. },
  139. 'toggle-tile-view': () => {
  140. sendAnalytics(createApiEvent('tile-view.toggled'));
  141. APP.store.dispatch(toggleTileView());
  142. },
  143. 'video-hangup': (showFeedbackDialog = true) => {
  144. sendAnalytics(createApiEvent('video.hangup'));
  145. APP.conference.hangup(showFeedbackDialog);
  146. },
  147. 'email': email => {
  148. sendAnalytics(createApiEvent('email.changed'));
  149. APP.conference.changeLocalEmail(email);
  150. },
  151. 'avatar-url': avatarUrl => {
  152. sendAnalytics(createApiEvent('avatar.url.changed'));
  153. APP.conference.changeLocalAvatarUrl(avatarUrl);
  154. },
  155. 'send-endpoint-text-message': (to, text) => {
  156. logger.debug('Send endpoint message command received');
  157. try {
  158. APP.conference.sendEndpointMessage(to, {
  159. name: ENDPOINT_TEXT_MESSAGE_NAME,
  160. text
  161. });
  162. } catch (err) {
  163. logger.error('Failed sending endpoint text message', err);
  164. }
  165. },
  166. 'e2ee-key': key => {
  167. logger.debug('Set E2EE key command received');
  168. APP.store.dispatch(setE2EEKey(key));
  169. },
  170. 'set-video-quality': frameHeight => {
  171. logger.debug('Set video quality command received');
  172. sendAnalytics(createApiEvent('set.video.quality'));
  173. APP.store.dispatch(setVideoQuality(frameHeight));
  174. }
  175. };
  176. transport.on('event', ({ data, name }) => {
  177. if (name && commands[name]) {
  178. commands[name](...data);
  179. return true;
  180. }
  181. return false;
  182. });
  183. transport.on('request', (request, callback) => {
  184. const { dispatch, getState } = APP.store;
  185. if (processExternalDeviceRequest(dispatch, getState, request, callback)) {
  186. return true;
  187. }
  188. const { name } = request;
  189. switch (name) {
  190. case 'invite': {
  191. const { invitees } = request;
  192. if (!Array.isArray(invitees) || invitees.length === 0) {
  193. callback({
  194. error: new Error('Unexpected format of invitees')
  195. });
  196. break;
  197. }
  198. // The store should be already available because API.init is called
  199. // on appWillMount action.
  200. APP.store.dispatch(
  201. invite(invitees, true))
  202. .then(failedInvitees => {
  203. let error;
  204. let result;
  205. if (failedInvitees.length) {
  206. error = new Error('One or more invites failed!');
  207. } else {
  208. result = true;
  209. }
  210. callback({
  211. error,
  212. result
  213. });
  214. });
  215. break;
  216. }
  217. case 'is-audio-muted':
  218. callback(APP.conference.isLocalAudioMuted());
  219. break;
  220. case 'is-video-muted':
  221. callback(APP.conference.isLocalVideoMuted());
  222. break;
  223. case 'is-audio-available':
  224. callback(audioAvailable);
  225. break;
  226. case 'is-video-available':
  227. callback(videoAvailable);
  228. break;
  229. default:
  230. return false;
  231. }
  232. return true;
  233. });
  234. }
  235. /**
  236. * Listens for desktop/screen sharing enabled events and toggles the screen
  237. * sharing if needed.
  238. *
  239. * @param {boolean} enabled - Current screen sharing enabled status.
  240. * @returns {void}
  241. */
  242. function onDesktopSharingEnabledChanged(enabled = false) {
  243. if (enabled && initialScreenSharingState) {
  244. toggleScreenSharing();
  245. }
  246. }
  247. /**
  248. * Check whether the API should be enabled or not.
  249. *
  250. * @returns {boolean}
  251. */
  252. function shouldBeEnabled() {
  253. return (
  254. typeof API_ID === 'number'
  255. // XXX Enable the API when a JSON Web Token (JWT) is specified in
  256. // the location/URL because then it is very likely that the Jitsi
  257. // Meet (Web) app is being used by an external/wrapping (Web) app
  258. // and, consequently, the latter will need to communicate with the
  259. // former. (The described logic is merely a heuristic though.)
  260. || parseJWTFromURLParams());
  261. }
  262. /**
  263. * Executes on toggle-share-screen command.
  264. *
  265. * @param {boolean} [enable] - Whether this toggle is to explicitly enable or
  266. * disable screensharing. If not defined, the application will automatically
  267. * attempt to toggle between enabled and disabled. This boolean is useful for
  268. * explicitly setting desired screensharing state.
  269. * @returns {void}
  270. */
  271. function toggleScreenSharing(enable) {
  272. if (APP.conference.isDesktopSharingEnabled) {
  273. // eslint-disable-next-line no-empty-function
  274. APP.conference.toggleScreenSharing(enable).catch(() => {});
  275. } else {
  276. initialScreenSharingState = !initialScreenSharingState;
  277. }
  278. }
  279. /**
  280. * Implements API class that communicates with external API class and provides
  281. * interface to access Jitsi Meet features by external applications that embed
  282. * Jitsi Meet.
  283. */
  284. class API {
  285. _enabled: boolean;
  286. /**
  287. * Initializes the API. Setups message event listeners that will receive
  288. * information from external applications that embed Jitsi Meet. It also
  289. * sends a message to the external application that API is initialized.
  290. *
  291. * @param {Object} options - Optional parameters.
  292. * @returns {void}
  293. */
  294. init() {
  295. if (!shouldBeEnabled()) {
  296. return;
  297. }
  298. /**
  299. * Current status (enabled/disabled) of API.
  300. *
  301. * @private
  302. * @type {boolean}
  303. */
  304. this._enabled = true;
  305. APP.conference.addListener(
  306. JitsiMeetConferenceEvents.DESKTOP_SHARING_ENABLED_CHANGED,
  307. onDesktopSharingEnabledChanged);
  308. initCommands();
  309. }
  310. /**
  311. * Notify external application (if API is enabled) that the large video
  312. * visibility changed.
  313. *
  314. * @param {boolean} isHidden - True if the large video is hidden and false
  315. * otherwise.
  316. * @returns {void}
  317. */
  318. notifyLargeVideoVisibilityChanged(isHidden: boolean) {
  319. this._sendEvent({
  320. name: 'large-video-visibility-changed',
  321. isVisible: !isHidden
  322. });
  323. }
  324. /**
  325. * Notifies the external application (spot) that the local jitsi-participant
  326. * has a status update.
  327. *
  328. * @param {Object} event - The message to pass onto spot.
  329. * @returns {void}
  330. */
  331. sendProxyConnectionEvent(event: Object) {
  332. this._sendEvent({
  333. name: 'proxy-connection-event',
  334. ...event
  335. });
  336. }
  337. /**
  338. * Sends event to the external application.
  339. *
  340. * @param {Object} event - The event to be sent.
  341. * @returns {void}
  342. */
  343. _sendEvent(event: Object = {}) {
  344. if (this._enabled) {
  345. transport.sendEvent(event);
  346. }
  347. }
  348. /**
  349. * Notify external application (if API is enabled) that message was sent.
  350. *
  351. * @param {string} message - Message body.
  352. * @param {boolean} privateMessage - True if the message was a private message.
  353. * @returns {void}
  354. */
  355. notifySendingChatMessage(message: string, privateMessage: boolean) {
  356. this._sendEvent({
  357. name: 'outgoing-message',
  358. message,
  359. privateMessage
  360. });
  361. }
  362. /**
  363. * Notify external application (if API is enabled) that message was
  364. * received.
  365. *
  366. * @param {Object} options - Object with the message properties.
  367. * @returns {void}
  368. */
  369. notifyReceivedChatMessage(
  370. { body, id, nick, ts }: {
  371. body: *, id: string, nick: string, ts: *
  372. } = {}) {
  373. if (APP.conference.isLocalId(id)) {
  374. return;
  375. }
  376. this._sendEvent({
  377. name: 'incoming-message',
  378. from: id,
  379. message: body,
  380. nick,
  381. stamp: ts
  382. });
  383. }
  384. /**
  385. * Notify external application (if API is enabled) that user joined the
  386. * conference.
  387. *
  388. * @param {string} id - User id.
  389. * @param {Object} props - The display name of the user.
  390. * @returns {void}
  391. */
  392. notifyUserJoined(id: string, props: Object) {
  393. this._sendEvent({
  394. name: 'participant-joined',
  395. id,
  396. ...props
  397. });
  398. }
  399. /**
  400. * Notify external application (if API is enabled) that user left the
  401. * conference.
  402. *
  403. * @param {string} id - User id.
  404. * @returns {void}
  405. */
  406. notifyUserLeft(id: string) {
  407. this._sendEvent({
  408. name: 'participant-left',
  409. id
  410. });
  411. }
  412. /**
  413. * Notify external application (if API is enabled) that the user role
  414. * has changed.
  415. *
  416. * @param {string} id - User id.
  417. * @param {string} role - The new user role.
  418. * @returns {void}
  419. */
  420. notifyUserRoleChanged(id: string, role: string) {
  421. this._sendEvent({
  422. name: 'participant-role-changed',
  423. id,
  424. role
  425. });
  426. }
  427. /**
  428. * Notify external application (if API is enabled) that user changed their
  429. * avatar.
  430. *
  431. * @param {string} id - User id.
  432. * @param {string} avatarURL - The new avatar URL of the participant.
  433. * @returns {void}
  434. */
  435. notifyAvatarChanged(id: string, avatarURL: string) {
  436. this._sendEvent({
  437. name: 'avatar-changed',
  438. avatarURL,
  439. id
  440. });
  441. }
  442. /**
  443. * Notify external application (if API is enabled) that user received
  444. * a text message through datachannels.
  445. *
  446. * @param {Object} data - The event data.
  447. * @returns {void}
  448. */
  449. notifyEndpointTextMessageReceived(data: Object) {
  450. this._sendEvent({
  451. name: 'endpoint-text-message-received',
  452. data
  453. });
  454. }
  455. /**
  456. * Notify external application (if API is enabled) that the device list has
  457. * changed.
  458. *
  459. * @param {Object} devices - The new device list.
  460. * @returns {void}
  461. */
  462. notifyDeviceListChanged(devices: Object) {
  463. this._sendEvent({
  464. name: 'device-list-changed',
  465. devices });
  466. }
  467. /**
  468. * Notify external application (if API is enabled) that user changed their
  469. * nickname.
  470. *
  471. * @param {string} id - User id.
  472. * @param {string} displayname - User nickname.
  473. * @param {string} formattedDisplayName - The display name shown in Jitsi
  474. * meet's UI for the user.
  475. * @returns {void}
  476. */
  477. notifyDisplayNameChanged(
  478. id: string,
  479. { displayName, formattedDisplayName }: Object) {
  480. this._sendEvent({
  481. name: 'display-name-change',
  482. displayname: displayName,
  483. formattedDisplayName,
  484. id
  485. });
  486. }
  487. /**
  488. * Notify external application (if API is enabled) that user changed their
  489. * email.
  490. *
  491. * @param {string} id - User id.
  492. * @param {string} email - The new email of the participant.
  493. * @returns {void}
  494. */
  495. notifyEmailChanged(
  496. id: string,
  497. { email }: Object) {
  498. this._sendEvent({
  499. name: 'email-change',
  500. email,
  501. id
  502. });
  503. }
  504. /**
  505. * Notify external application (if API is enabled) that the conference has
  506. * been joined.
  507. *
  508. * @param {string} roomName - The room name.
  509. * @param {string} id - The id of the local user.
  510. * @param {Object} props - The display name and avatar URL of the local
  511. * user.
  512. * @returns {void}
  513. */
  514. notifyConferenceJoined(roomName: string, id: string, props: Object) {
  515. this._sendEvent({
  516. name: 'video-conference-joined',
  517. roomName,
  518. id,
  519. ...props
  520. });
  521. }
  522. /**
  523. * Notify external application (if API is enabled) that user changed their
  524. * nickname.
  525. *
  526. * @param {string} roomName - User id.
  527. * @returns {void}
  528. */
  529. notifyConferenceLeft(roomName: string) {
  530. this._sendEvent({
  531. name: 'video-conference-left',
  532. roomName
  533. });
  534. }
  535. /**
  536. * Notify external application (if API is enabled) that we are ready to be
  537. * closed.
  538. *
  539. * @returns {void}
  540. */
  541. notifyReadyToClose() {
  542. this._sendEvent({ name: 'video-ready-to-close' });
  543. }
  544. /**
  545. * Notify external application (if API is enabled) that a suspend event in host computer.
  546. *
  547. * @returns {void}
  548. */
  549. notifySuspendDetected() {
  550. this._sendEvent({ name: 'suspend-detected' });
  551. }
  552. /**
  553. * Notify external application (if API is enabled) for audio muted status
  554. * changed.
  555. *
  556. * @param {boolean} muted - The new muted status.
  557. * @returns {void}
  558. */
  559. notifyAudioMutedStatusChanged(muted: boolean) {
  560. this._sendEvent({
  561. name: 'audio-mute-status-changed',
  562. muted
  563. });
  564. }
  565. /**
  566. * Notify external application (if API is enabled) for video muted status
  567. * changed.
  568. *
  569. * @param {boolean} muted - The new muted status.
  570. * @returns {void}
  571. */
  572. notifyVideoMutedStatusChanged(muted: boolean) {
  573. this._sendEvent({
  574. name: 'video-mute-status-changed',
  575. muted
  576. });
  577. }
  578. /**
  579. * Notify external application (if API is enabled) for audio availability
  580. * changed.
  581. *
  582. * @param {boolean} available - True if available and false otherwise.
  583. * @returns {void}
  584. */
  585. notifyAudioAvailabilityChanged(available: boolean) {
  586. audioAvailable = available;
  587. this._sendEvent({
  588. name: 'audio-availability-changed',
  589. available
  590. });
  591. }
  592. /**
  593. * Notify external application (if API is enabled) for video available
  594. * status changed.
  595. *
  596. * @param {boolean} available - True if available and false otherwise.
  597. * @returns {void}
  598. */
  599. notifyVideoAvailabilityChanged(available: boolean) {
  600. videoAvailable = available;
  601. this._sendEvent({
  602. name: 'video-availability-changed',
  603. available
  604. });
  605. }
  606. /**
  607. * Notify external application (if API is enabled) that the on stage
  608. * participant has changed.
  609. *
  610. * @param {string} id - User id of the new on stage participant.
  611. * @returns {void}
  612. */
  613. notifyOnStageParticipantChanged(id: string) {
  614. this._sendEvent({
  615. name: 'on-stage-participant-changed',
  616. id
  617. });
  618. }
  619. /**
  620. * Notify external application of an unexpected camera-related error having
  621. * occurred.
  622. *
  623. * @param {string} type - The type of the camera error.
  624. * @param {string} message - Additional information about the error.
  625. * @returns {void}
  626. */
  627. notifyOnCameraError(type: string, message: string) {
  628. this._sendEvent({
  629. name: 'camera-error',
  630. type,
  631. message
  632. });
  633. }
  634. /**
  635. * Notify external application of an unexpected mic-related error having
  636. * occurred.
  637. *
  638. * @param {string} type - The type of the mic error.
  639. * @param {string} message - Additional information about the error.
  640. * @returns {void}
  641. */
  642. notifyOnMicError(type: string, message: string) {
  643. this._sendEvent({
  644. name: 'mic-error',
  645. type,
  646. message
  647. });
  648. }
  649. /**
  650. * Notify external application (if API is enabled) that conference feedback
  651. * has been submitted. Intended to be used in conjunction with the
  652. * submit-feedback command to get notified if feedback was submitted.
  653. *
  654. * @param {string} error - A failure message, if any.
  655. * @returns {void}
  656. */
  657. notifyFeedbackSubmitted(error: string) {
  658. this._sendEvent({
  659. name: 'feedback-submitted',
  660. error
  661. });
  662. }
  663. /**
  664. * Notify external application (if API is enabled) that the feedback prompt
  665. * has been displayed.
  666. *
  667. * @returns {void}
  668. */
  669. notifyFeedbackPromptDisplayed() {
  670. this._sendEvent({ name: 'feedback-prompt-displayed' });
  671. }
  672. /**
  673. * Notify external application (if API is enabled) that the display
  674. * configuration of the filmstrip has been changed.
  675. *
  676. * @param {boolean} visible - Whether or not the filmstrip has been set to
  677. * be displayed or hidden.
  678. * @returns {void}
  679. */
  680. notifyFilmstripDisplayChanged(visible: boolean) {
  681. this._sendEvent({
  682. name: 'filmstrip-display-changed',
  683. visible
  684. });
  685. }
  686. /**
  687. * Notify external application of a participant, remote or local, being
  688. * removed from the conference by another participant.
  689. *
  690. * @param {string} kicked - The ID of the participant removed from the
  691. * conference.
  692. * @param {string} kicker - The ID of the participant that removed the
  693. * other participant.
  694. * @returns {void}
  695. */
  696. notifyKickedOut(kicked: Object, kicker: Object) {
  697. this._sendEvent({
  698. name: 'participant-kicked-out',
  699. kicked,
  700. kicker
  701. });
  702. }
  703. /**
  704. * Notify external application of the current meeting requiring a password
  705. * to join.
  706. *
  707. * @returns {void}
  708. */
  709. notifyOnPasswordRequired() {
  710. this._sendEvent({ name: 'password-required' });
  711. }
  712. /**
  713. * Notify external application (if API is enabled) that the screen sharing
  714. * has been turned on/off.
  715. *
  716. * @param {boolean} on - True if screen sharing is enabled.
  717. * @param {Object} details - Additional information about the screen
  718. * sharing.
  719. * @param {string} details.sourceType - Type of device or window the screen
  720. * share is capturing.
  721. * @returns {void}
  722. */
  723. notifyScreenSharingStatusChanged(on: boolean, details: Object) {
  724. this._sendEvent({
  725. name: 'screen-sharing-status-changed',
  726. on,
  727. details
  728. });
  729. }
  730. /**
  731. * Notify external application (if API is enabled) that the dominant speaker
  732. * has been turned on/off.
  733. *
  734. * @param {string} id - Id of the dominant participant.
  735. * @returns {void}
  736. */
  737. notifyDominantSpeakerChanged(id: string) {
  738. this._sendEvent({
  739. name: 'dominant-speaker-changed',
  740. id
  741. });
  742. }
  743. /**
  744. * Notify external application (if API is enabled) that the conference
  745. * changed their subject.
  746. *
  747. * @param {string} subject - Conference subject.
  748. * @returns {void}
  749. */
  750. notifySubjectChanged(subject: string) {
  751. this._sendEvent({
  752. name: 'subject-change',
  753. subject
  754. });
  755. }
  756. /**
  757. * Notify external application (if API is enabled) that tile view has been
  758. * entered or exited.
  759. *
  760. * @param {string} enabled - True if tile view is currently displayed, false
  761. * otherwise.
  762. * @returns {void}
  763. */
  764. notifyTileViewChanged(enabled: boolean) {
  765. this._sendEvent({
  766. name: 'tile-view-changed',
  767. enabled
  768. });
  769. }
  770. /**
  771. * Disposes the allocated resources.
  772. *
  773. * @returns {void}
  774. */
  775. dispose() {
  776. if (this._enabled) {
  777. this._enabled = false;
  778. APP.conference.removeListener(
  779. JitsiMeetConferenceEvents.DESKTOP_SHARING_ENABLED_CHANGED,
  780. onDesktopSharingEnabledChanged);
  781. }
  782. }
  783. }
  784. export default new API();