您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074
  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. case 'get-content-sharing-participants': {
  370. const tracks = getState()['features/base/tracks'];
  371. const sharingParticipantIds = tracks.filter(tr => tr.videoType === 'desktop').map(t => t.participantId);
  372. callback({
  373. sharingParticipantIds
  374. });
  375. break;
  376. }
  377. default:
  378. return false;
  379. }
  380. return true;
  381. });
  382. }
  383. /**
  384. * Check whether the API should be enabled or not.
  385. *
  386. * @returns {boolean}
  387. */
  388. function shouldBeEnabled() {
  389. return (
  390. typeof API_ID === 'number'
  391. // XXX Enable the API when a JSON Web Token (JWT) is specified in
  392. // the location/URL because then it is very likely that the Jitsi
  393. // Meet (Web) app is being used by an external/wrapping (Web) app
  394. // and, consequently, the latter will need to communicate with the
  395. // former. (The described logic is merely a heuristic though.)
  396. || parseJWTFromURLParams());
  397. }
  398. /**
  399. * Executes on toggle-share-screen command.
  400. *
  401. * @param {boolean} [enable] - Whether this toggle is to explicitly enable or
  402. * disable screensharing. If not defined, the application will automatically
  403. * attempt to toggle between enabled and disabled. This boolean is useful for
  404. * explicitly setting desired screensharing state.
  405. * @returns {void}
  406. */
  407. function toggleScreenSharing(enable) {
  408. if (JitsiMeetJS.isDesktopSharingEnabled()) {
  409. APP.conference.toggleScreenSharing(enable).catch(() => {
  410. logger.warn('Failed to toggle screen-sharing');
  411. });
  412. }
  413. }
  414. /**
  415. * Implements API class that communicates with external API class and provides
  416. * interface to access Jitsi Meet features by external applications that embed
  417. * Jitsi Meet.
  418. */
  419. class API {
  420. _enabled: boolean;
  421. /**
  422. * Initializes the API. Setups message event listeners that will receive
  423. * information from external applications that embed Jitsi Meet. It also
  424. * sends a message to the external application that API is initialized.
  425. *
  426. * @param {Object} options - Optional parameters.
  427. * @returns {void}
  428. */
  429. init() {
  430. if (!shouldBeEnabled()) {
  431. return;
  432. }
  433. /**
  434. * Current status (enabled/disabled) of API.
  435. *
  436. * @private
  437. * @type {boolean}
  438. */
  439. this._enabled = true;
  440. initCommands();
  441. }
  442. /**
  443. * Notify external application (if API is enabled) that the large video
  444. * visibility changed.
  445. *
  446. * @param {boolean} isHidden - True if the large video is hidden and false
  447. * otherwise.
  448. * @returns {void}
  449. */
  450. notifyLargeVideoVisibilityChanged(isHidden: boolean) {
  451. this._sendEvent({
  452. name: 'large-video-visibility-changed',
  453. isVisible: !isHidden
  454. });
  455. }
  456. /**
  457. * Notifies the external application (spot) that the local jitsi-participant
  458. * has a status update.
  459. *
  460. * @param {Object} event - The message to pass onto spot.
  461. * @returns {void}
  462. */
  463. sendProxyConnectionEvent(event: Object) {
  464. this._sendEvent({
  465. name: 'proxy-connection-event',
  466. ...event
  467. });
  468. }
  469. /**
  470. * Sends event to the external application.
  471. *
  472. * @param {Object} event - The event to be sent.
  473. * @returns {void}
  474. */
  475. _sendEvent(event: Object = {}) {
  476. if (this._enabled) {
  477. transport.sendEvent(event);
  478. }
  479. }
  480. /**
  481. * Notify external application (if API is enabled) that message was sent.
  482. *
  483. * @param {string} message - Message body.
  484. * @param {boolean} privateMessage - True if the message was a private message.
  485. * @returns {void}
  486. */
  487. notifySendingChatMessage(message: string, privateMessage: boolean) {
  488. this._sendEvent({
  489. name: 'outgoing-message',
  490. message,
  491. privateMessage
  492. });
  493. }
  494. /**
  495. * Notify external application that the video quality setting has changed.
  496. *
  497. * @param {number} videoQuality - The video quality. The number represents the maximum height of the video streams.
  498. * @returns {void}
  499. */
  500. notifyVideoQualityChanged(videoQuality: number) {
  501. this._sendEvent({
  502. name: 'video-quality-changed',
  503. videoQuality
  504. });
  505. }
  506. /**
  507. * Notify external application (if API is enabled) that message was
  508. * received.
  509. *
  510. * @param {Object} options - Object with the message properties.
  511. * @returns {void}
  512. */
  513. notifyReceivedChatMessage(
  514. { body, id, nick, ts }: {
  515. body: *, id: string, nick: string, ts: *
  516. } = {}) {
  517. if (APP.conference.isLocalId(id)) {
  518. return;
  519. }
  520. this._sendEvent({
  521. name: 'incoming-message',
  522. from: id,
  523. message: body,
  524. nick,
  525. stamp: ts
  526. });
  527. }
  528. /**
  529. * Notify external application (if API is enabled) that user joined the
  530. * conference.
  531. *
  532. * @param {string} id - User id.
  533. * @param {Object} props - The display name of the user.
  534. * @returns {void}
  535. */
  536. notifyUserJoined(id: string, props: Object) {
  537. this._sendEvent({
  538. name: 'participant-joined',
  539. id,
  540. ...props
  541. });
  542. }
  543. /**
  544. * Notify external application (if API is enabled) that user left the
  545. * conference.
  546. *
  547. * @param {string} id - User id.
  548. * @returns {void}
  549. */
  550. notifyUserLeft(id: string) {
  551. this._sendEvent({
  552. name: 'participant-left',
  553. id
  554. });
  555. }
  556. /**
  557. * Notify external application (if API is enabled) that the user role
  558. * has changed.
  559. *
  560. * @param {string} id - User id.
  561. * @param {string} role - The new user role.
  562. * @returns {void}
  563. */
  564. notifyUserRoleChanged(id: string, role: string) {
  565. this._sendEvent({
  566. name: 'participant-role-changed',
  567. id,
  568. role
  569. });
  570. }
  571. /**
  572. * Notify external application (if API is enabled) that user changed their
  573. * avatar.
  574. *
  575. * @param {string} id - User id.
  576. * @param {string} avatarURL - The new avatar URL of the participant.
  577. * @returns {void}
  578. */
  579. notifyAvatarChanged(id: string, avatarURL: string) {
  580. this._sendEvent({
  581. name: 'avatar-changed',
  582. avatarURL,
  583. id
  584. });
  585. }
  586. /**
  587. * Notify external application (if API is enabled) that user received
  588. * a text message through datachannels.
  589. *
  590. * @param {Object} data - The event data.
  591. * @returns {void}
  592. */
  593. notifyEndpointTextMessageReceived(data: Object) {
  594. this._sendEvent({
  595. name: 'endpoint-text-message-received',
  596. data
  597. });
  598. }
  599. /**
  600. * Notify external application (if API is enabled) that the list of sharing participants changed.
  601. *
  602. * @param {Object} data - The event data.
  603. * @returns {void}
  604. */
  605. notifySharingParticipantsChanged(data: Object) {
  606. this._sendEvent({
  607. name: 'content-sharing-participants-changed',
  608. data
  609. });
  610. }
  611. /**
  612. * Notify external application (if API is enabled) that the device list has
  613. * changed.
  614. *
  615. * @param {Object} devices - The new device list.
  616. * @returns {void}
  617. */
  618. notifyDeviceListChanged(devices: Object) {
  619. this._sendEvent({
  620. name: 'device-list-changed',
  621. devices
  622. });
  623. }
  624. /**
  625. * Notify external application (if API is enabled) that user changed their
  626. * nickname.
  627. *
  628. * @param {string} id - User id.
  629. * @param {string} displayname - User nickname.
  630. * @param {string} formattedDisplayName - The display name shown in Jitsi
  631. * meet's UI for the user.
  632. * @returns {void}
  633. */
  634. notifyDisplayNameChanged(
  635. id: string,
  636. { displayName, formattedDisplayName }: Object) {
  637. this._sendEvent({
  638. name: 'display-name-change',
  639. displayname: displayName,
  640. formattedDisplayName,
  641. id
  642. });
  643. }
  644. /**
  645. * Notify external application (if API is enabled) that user changed their
  646. * email.
  647. *
  648. * @param {string} id - User id.
  649. * @param {string} email - The new email of the participant.
  650. * @returns {void}
  651. */
  652. notifyEmailChanged(
  653. id: string,
  654. { email }: Object) {
  655. this._sendEvent({
  656. name: 'email-change',
  657. email,
  658. id
  659. });
  660. }
  661. /**
  662. * Notify external application (if API is enabled) that the an error has been logged.
  663. *
  664. * @param {string} logLevel - The message log level.
  665. * @param {Array} args - Array of strings composing the log message.
  666. * @returns {void}
  667. */
  668. notifyLog(logLevel: string, args: Array<string>) {
  669. this._sendEvent({
  670. name: 'log',
  671. logLevel,
  672. args
  673. });
  674. }
  675. /**
  676. * Notify external application (if API is enabled) that the conference has
  677. * been joined.
  678. *
  679. * @param {string} roomName - The room name.
  680. * @param {string} id - The id of the local user.
  681. * @param {Object} props - The display name and avatar URL of the local
  682. * user.
  683. * @returns {void}
  684. */
  685. notifyConferenceJoined(roomName: string, id: string, props: Object) {
  686. this._sendEvent({
  687. name: 'video-conference-joined',
  688. roomName,
  689. id,
  690. ...props
  691. });
  692. }
  693. /**
  694. * Notify external application (if API is enabled) that local user has left the conference.
  695. *
  696. * @param {string} roomName - User id.
  697. * @returns {void}
  698. */
  699. notifyConferenceLeft(roomName: string) {
  700. this._sendEvent({
  701. name: 'video-conference-left',
  702. roomName
  703. });
  704. }
  705. /**
  706. * Notify external application (if API is enabled) that we are ready to be
  707. * closed.
  708. *
  709. * @returns {void}
  710. */
  711. notifyReadyToClose() {
  712. this._sendEvent({ name: 'video-ready-to-close' });
  713. }
  714. /**
  715. * Notify external application (if API is enabled) that a suspend event in host computer.
  716. *
  717. * @returns {void}
  718. */
  719. notifySuspendDetected() {
  720. this._sendEvent({ name: 'suspend-detected' });
  721. }
  722. /**
  723. * Notify external application (if API is enabled) for audio muted status
  724. * changed.
  725. *
  726. * @param {boolean} muted - The new muted status.
  727. * @returns {void}
  728. */
  729. notifyAudioMutedStatusChanged(muted: boolean) {
  730. this._sendEvent({
  731. name: 'audio-mute-status-changed',
  732. muted
  733. });
  734. }
  735. /**
  736. * Notify external application (if API is enabled) for video muted status
  737. * changed.
  738. *
  739. * @param {boolean} muted - The new muted status.
  740. * @returns {void}
  741. */
  742. notifyVideoMutedStatusChanged(muted: boolean) {
  743. this._sendEvent({
  744. name: 'video-mute-status-changed',
  745. muted
  746. });
  747. }
  748. /**
  749. * Notify external application (if API is enabled) for audio availability
  750. * changed.
  751. *
  752. * @param {boolean} available - True if available and false otherwise.
  753. * @returns {void}
  754. */
  755. notifyAudioAvailabilityChanged(available: boolean) {
  756. audioAvailable = available;
  757. this._sendEvent({
  758. name: 'audio-availability-changed',
  759. available
  760. });
  761. }
  762. /**
  763. * Notify external application (if API is enabled) for video available
  764. * status changed.
  765. *
  766. * @param {boolean} available - True if available and false otherwise.
  767. * @returns {void}
  768. */
  769. notifyVideoAvailabilityChanged(available: boolean) {
  770. videoAvailable = available;
  771. this._sendEvent({
  772. name: 'video-availability-changed',
  773. available
  774. });
  775. }
  776. /**
  777. * Notify external application (if API is enabled) that the on stage
  778. * participant has changed.
  779. *
  780. * @param {string} id - User id of the new on stage participant.
  781. * @returns {void}
  782. */
  783. notifyOnStageParticipantChanged(id: string) {
  784. this._sendEvent({
  785. name: 'on-stage-participant-changed',
  786. id
  787. });
  788. }
  789. /**
  790. * Notify external application of an unexpected camera-related error having
  791. * occurred.
  792. *
  793. * @param {string} type - The type of the camera error.
  794. * @param {string} message - Additional information about the error.
  795. * @returns {void}
  796. */
  797. notifyOnCameraError(type: string, message: string) {
  798. this._sendEvent({
  799. name: 'camera-error',
  800. type,
  801. message
  802. });
  803. }
  804. /**
  805. * Notify external application of an unexpected mic-related error having
  806. * occurred.
  807. *
  808. * @param {string} type - The type of the mic error.
  809. * @param {string} message - Additional information about the error.
  810. * @returns {void}
  811. */
  812. notifyOnMicError(type: string, message: string) {
  813. this._sendEvent({
  814. name: 'mic-error',
  815. type,
  816. message
  817. });
  818. }
  819. /**
  820. * Notify external application (if API is enabled) that conference feedback
  821. * has been submitted. Intended to be used in conjunction with the
  822. * submit-feedback command to get notified if feedback was submitted.
  823. *
  824. * @param {string} error - A failure message, if any.
  825. * @returns {void}
  826. */
  827. notifyFeedbackSubmitted(error: string) {
  828. this._sendEvent({
  829. name: 'feedback-submitted',
  830. error
  831. });
  832. }
  833. /**
  834. * Notify external application (if API is enabled) that the feedback prompt
  835. * has been displayed.
  836. *
  837. * @returns {void}
  838. */
  839. notifyFeedbackPromptDisplayed() {
  840. this._sendEvent({ name: 'feedback-prompt-displayed' });
  841. }
  842. /**
  843. * Notify external application (if API is enabled) that the display
  844. * configuration of the filmstrip has been changed.
  845. *
  846. * @param {boolean} visible - Whether or not the filmstrip has been set to
  847. * be displayed or hidden.
  848. * @returns {void}
  849. */
  850. notifyFilmstripDisplayChanged(visible: boolean) {
  851. this._sendEvent({
  852. name: 'filmstrip-display-changed',
  853. visible
  854. });
  855. }
  856. /**
  857. * Notify external application of a participant, remote or local, being
  858. * removed from the conference by another participant.
  859. *
  860. * @param {string} kicked - The ID of the participant removed from the
  861. * conference.
  862. * @param {string} kicker - The ID of the participant that removed the
  863. * other participant.
  864. * @returns {void}
  865. */
  866. notifyKickedOut(kicked: Object, kicker: Object) {
  867. this._sendEvent({
  868. name: 'participant-kicked-out',
  869. kicked,
  870. kicker
  871. });
  872. }
  873. /**
  874. * Notify external application of the current meeting requiring a password
  875. * to join.
  876. *
  877. * @returns {void}
  878. */
  879. notifyOnPasswordRequired() {
  880. this._sendEvent({ name: 'password-required' });
  881. }
  882. /**
  883. * Notify external application (if API is enabled) that the screen sharing
  884. * has been turned on/off.
  885. *
  886. * @param {boolean} on - True if screen sharing is enabled.
  887. * @param {Object} details - Additional information about the screen
  888. * sharing.
  889. * @param {string} details.sourceType - Type of device or window the screen
  890. * share is capturing.
  891. * @returns {void}
  892. */
  893. notifyScreenSharingStatusChanged(on: boolean, details: Object) {
  894. this._sendEvent({
  895. name: 'screen-sharing-status-changed',
  896. on,
  897. details
  898. });
  899. }
  900. /**
  901. * Notify external application (if API is enabled) that the dominant speaker
  902. * has been turned on/off.
  903. *
  904. * @param {string} id - Id of the dominant participant.
  905. * @returns {void}
  906. */
  907. notifyDominantSpeakerChanged(id: string) {
  908. this._sendEvent({
  909. name: 'dominant-speaker-changed',
  910. id
  911. });
  912. }
  913. /**
  914. * Notify external application (if API is enabled) that the conference
  915. * changed their subject.
  916. *
  917. * @param {string} subject - Conference subject.
  918. * @returns {void}
  919. */
  920. notifySubjectChanged(subject: string) {
  921. this._sendEvent({
  922. name: 'subject-change',
  923. subject
  924. });
  925. }
  926. /**
  927. * Notify external application (if API is enabled) that tile view has been
  928. * entered or exited.
  929. *
  930. * @param {string} enabled - True if tile view is currently displayed, false
  931. * otherwise.
  932. * @returns {void}
  933. */
  934. notifyTileViewChanged(enabled: boolean) {
  935. this._sendEvent({
  936. name: 'tile-view-changed',
  937. enabled
  938. });
  939. }
  940. /**
  941. * Notify external application (if API is enabled) that the localStorage has changed.
  942. *
  943. * @param {string} localStorageContent - The new localStorageContent.
  944. * @returns {void}
  945. */
  946. notifyLocalStorageChanged(localStorageContent: string) {
  947. this._sendEvent({
  948. name: 'local-storage-changed',
  949. localStorageContent
  950. });
  951. }
  952. /**
  953. * Notify external application (if API is enabled) that user updated their hand raised.
  954. *
  955. * @param {string} id - User id.
  956. * @param {boolean} handRaised - Whether user has raised hand.
  957. * @returns {void}
  958. */
  959. notifyRaiseHandUpdated(id: string, handRaised: boolean) {
  960. this._sendEvent({
  961. name: 'raise-hand-updated',
  962. handRaised,
  963. id
  964. });
  965. }
  966. /**
  967. * Disposes the allocated resources.
  968. *
  969. * @returns {void}
  970. */
  971. dispose() {
  972. if (this._enabled) {
  973. this._enabled = false;
  974. }
  975. }
  976. }
  977. export default new API();