Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

API.js 31KB

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