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

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