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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. // @flow
  2. import * as JitsiMeetConferenceEvents from '../../ConferenceEvents';
  3. import { parseJWTFromURLParams } from '../../react/features/base/jwt';
  4. import { sendEvent } from '../../react/features/analytics';
  5. import { getJitsiMeetTransport } from '../transport';
  6. import { API_ID } from './constants';
  7. const logger = require('jitsi-meet-logger').getLogger(__filename);
  8. declare var APP: Object;
  9. /**
  10. * List of the available commands.
  11. */
  12. let commands = {};
  13. /**
  14. * The state of screen sharing(started/stopped) before the screen sharing is
  15. * enabled and initialized.
  16. * NOTE: This flag help us to cache the state and use it if toggle-share-screen
  17. * was received before the initialization.
  18. */
  19. let initialScreenSharingState = false;
  20. /**
  21. * The transport instance used for communication with external apps.
  22. *
  23. * @type {Transport}
  24. */
  25. const transport = getJitsiMeetTransport();
  26. /**
  27. * The current audio availability.
  28. *
  29. * @type {boolean}
  30. */
  31. let audioAvailable = true;
  32. /**
  33. * The current video availability.
  34. *
  35. * @type {boolean}
  36. */
  37. let videoAvailable = true;
  38. /**
  39. * Initializes supported commands.
  40. *
  41. * @returns {void}
  42. */
  43. function initCommands() {
  44. commands = {
  45. 'display-name':
  46. APP.conference.changeLocalDisplayName.bind(APP.conference),
  47. 'toggle-audio': () => {
  48. sendEvent('api.toggle.audio');
  49. logger.log('Audio toggle: API command received');
  50. APP.conference.toggleAudioMuted(false /* no UI */);
  51. },
  52. 'toggle-video': () => {
  53. sendEvent('api.toggle.video');
  54. logger.log('Video toggle: API command received');
  55. APP.conference.toggleVideoMuted(false /* no UI */);
  56. },
  57. 'toggle-film-strip': APP.UI.toggleFilmstrip,
  58. 'toggle-chat': APP.UI.toggleChat,
  59. 'toggle-contact-list': APP.UI.toggleContactList,
  60. 'toggle-share-screen': toggleScreenSharing,
  61. 'video-hangup': () => APP.conference.hangup(),
  62. 'email': APP.conference.changeLocalEmail,
  63. 'avatar-url': APP.conference.changeLocalAvatarUrl
  64. };
  65. transport.on('event', ({ data, name }) => {
  66. if (name && commands[name]) {
  67. commands[name](...data);
  68. return true;
  69. }
  70. return false;
  71. });
  72. transport.on('request', ({ name }, callback) => {
  73. switch (name) {
  74. case 'is-audio-muted':
  75. callback(APP.conference.isLocalAudioMuted());
  76. break;
  77. case 'is-video-muted':
  78. callback(APP.conference.isLocalVideoMuted());
  79. break;
  80. case 'is-audio-available':
  81. callback(audioAvailable);
  82. break;
  83. case 'is-video-available':
  84. callback(videoAvailable);
  85. break;
  86. default:
  87. return false;
  88. }
  89. return true;
  90. });
  91. }
  92. /**
  93. * Listens for desktop/screen sharing enabled events and toggles the screen
  94. * sharing if needed.
  95. *
  96. * @param {boolean} enabled - Current screen sharing enabled status.
  97. * @returns {void}
  98. */
  99. function onDesktopSharingEnabledChanged(enabled = false) {
  100. if (enabled && initialScreenSharingState) {
  101. toggleScreenSharing();
  102. }
  103. }
  104. /**
  105. * Check whether the API should be enabled or not.
  106. *
  107. * @returns {boolean}
  108. */
  109. function shouldBeEnabled() {
  110. return (
  111. typeof API_ID === 'number'
  112. // XXX Enable the API when a JSON Web Token (JWT) is specified in
  113. // the location/URL because then it is very likely that the Jitsi
  114. // Meet (Web) app is being used by an external/wrapping (Web) app
  115. // and, consequently, the latter will need to communicate with the
  116. // former. (The described logic is merely a heuristic though.)
  117. || parseJWTFromURLParams());
  118. }
  119. /**
  120. * Executes on toggle-share-screen command.
  121. *
  122. * @returns {void}
  123. */
  124. function toggleScreenSharing() {
  125. if (APP.conference.isDesktopSharingEnabled) {
  126. // eslint-disable-next-line no-empty-function
  127. APP.conference.toggleScreenSharing().catch(() => {});
  128. } else {
  129. initialScreenSharingState = !initialScreenSharingState;
  130. }
  131. }
  132. /**
  133. * Implements API class that communicates with external API class and provides
  134. * interface to access Jitsi Meet features by external applications that embed
  135. * Jitsi Meet.
  136. */
  137. class API {
  138. _enabled: boolean;
  139. /**
  140. * Initializes the API. Setups message event listeners that will receive
  141. * information from external applications that embed Jitsi Meet. It also
  142. * sends a message to the external application that API is initialized.
  143. *
  144. * @param {Object} options - Optional parameters.
  145. * @returns {void}
  146. */
  147. init() {
  148. if (!shouldBeEnabled()) {
  149. return;
  150. }
  151. /**
  152. * Current status (enabled/disabled) of API.
  153. *
  154. * @private
  155. * @type {boolean}
  156. */
  157. this._enabled = true;
  158. APP.conference.addListener(
  159. JitsiMeetConferenceEvents.DESKTOP_SHARING_ENABLED_CHANGED,
  160. onDesktopSharingEnabledChanged);
  161. initCommands();
  162. }
  163. /**
  164. * Sends event to the external application.
  165. *
  166. * @param {Object} event - The event to be sent.
  167. * @returns {void}
  168. */
  169. _sendEvent(event: Object = {}) {
  170. if (this._enabled) {
  171. transport.sendEvent(event);
  172. }
  173. }
  174. /**
  175. * Notify external application (if API is enabled) that message was sent.
  176. *
  177. * @param {string} message - Message body.
  178. * @returns {void}
  179. */
  180. notifySendingChatMessage(message: string) {
  181. this._sendEvent({
  182. name: 'outgoing-message',
  183. message
  184. });
  185. }
  186. /**
  187. * Notify external application (if API is enabled) that message was
  188. * received.
  189. *
  190. * @param {Object} options - Object with the message properties.
  191. * @returns {void}
  192. */
  193. notifyReceivedChatMessage(
  194. { body, id, nick, ts }: {
  195. body: *, id: string, nick: string, ts: *
  196. } = {}) {
  197. if (APP.conference.isLocalId(id)) {
  198. return;
  199. }
  200. this._sendEvent({
  201. name: 'incoming-message',
  202. from: id,
  203. message: body,
  204. nick,
  205. stamp: ts
  206. });
  207. }
  208. /**
  209. * Notify external application (if API is enabled) that user joined the
  210. * conference.
  211. *
  212. * @param {string} id - User id.
  213. * @returns {void}
  214. */
  215. notifyUserJoined(id: string) {
  216. this._sendEvent({
  217. name: 'participant-joined',
  218. id
  219. });
  220. }
  221. /**
  222. * Notify external application (if API is enabled) that user left the
  223. * conference.
  224. *
  225. * @param {string} id - User id.
  226. * @returns {void}
  227. */
  228. notifyUserLeft(id: string) {
  229. this._sendEvent({
  230. name: 'participant-left',
  231. id
  232. });
  233. }
  234. /**
  235. * Notify external application (if API is enabled) that user changed their
  236. * nickname.
  237. *
  238. * @param {string} id - User id.
  239. * @param {string} displayname - User nickname.
  240. * @returns {void}
  241. */
  242. notifyDisplayNameChanged(id: string, displayname: string) {
  243. this._sendEvent({
  244. name: 'display-name-change',
  245. displayname,
  246. id
  247. });
  248. }
  249. /**
  250. * Notify external application (if API is enabled) that the conference has
  251. * been joined.
  252. *
  253. * @param {string} roomName - The room name.
  254. * @returns {void}
  255. */
  256. notifyConferenceJoined(roomName: string) {
  257. this._sendEvent({
  258. name: 'video-conference-joined',
  259. roomName
  260. });
  261. }
  262. /**
  263. * Notify external application (if API is enabled) that user changed their
  264. * nickname.
  265. *
  266. * @param {string} roomName - User id.
  267. * @returns {void}
  268. */
  269. notifyConferenceLeft(roomName: string) {
  270. this._sendEvent({
  271. name: 'video-conference-left',
  272. roomName
  273. });
  274. }
  275. /**
  276. * Notify external application (if API is enabled) that we are ready to be
  277. * closed.
  278. *
  279. * @returns {void}
  280. */
  281. notifyReadyToClose() {
  282. this._sendEvent({ name: 'video-ready-to-close' });
  283. }
  284. /**
  285. * Notify external application (if API is enabled) for audio muted status
  286. * changed.
  287. *
  288. * @param {boolean} muted - The new muted status.
  289. * @returns {void}
  290. */
  291. notifyAudioMutedStatusChanged(muted: boolean) {
  292. this._sendEvent({
  293. name: 'audio-mute-status-changed',
  294. muted
  295. });
  296. }
  297. /**
  298. * Notify external application (if API is enabled) for video muted status
  299. * changed.
  300. *
  301. * @param {boolean} muted - The new muted status.
  302. * @returns {void}
  303. */
  304. notifyVideoMutedStatusChanged(muted: boolean) {
  305. this._sendEvent({
  306. name: 'video-mute-status-changed',
  307. muted
  308. });
  309. }
  310. /**
  311. * Notify external application (if API is enabled) for audio availability
  312. * changed.
  313. *
  314. * @param {boolean} available - True if available and false otherwise.
  315. * @returns {void}
  316. */
  317. notifyAudioAvailabilityChanged(available: boolean) {
  318. audioAvailable = available;
  319. this._sendEvent({
  320. name: 'audio-availability-changed',
  321. available
  322. });
  323. }
  324. /**
  325. * Notify external application (if API is enabled) for video available
  326. * status changed.
  327. *
  328. * @param {boolean} available - True if available and false otherwise.
  329. * @returns {void}
  330. */
  331. notifyVideoAvailabilityChanged(available: boolean) {
  332. videoAvailable = available;
  333. this._sendEvent({
  334. name: 'video-availability-changed',
  335. available
  336. });
  337. }
  338. /**
  339. * Disposes the allocated resources.
  340. *
  341. * @returns {void}
  342. */
  343. dispose() {
  344. if (this._enabled) {
  345. this._enabled = false;
  346. APP.conference.removeListener(
  347. JitsiMeetConferenceEvents.DESKTOP_SHARING_ENABLED_CHANGED,
  348. onDesktopSharingEnabledChanged);
  349. }
  350. }
  351. }
  352. export default new API();