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 29KB

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