Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

conference.js 27KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832
  1. /* global $, APP, JitsiMeetJS, config, interfaceConfig */
  2. import {openConnection} from './connection';
  3. //FIXME:
  4. import createRoomLocker from './modules/UI/authentication/RoomLocker';
  5. //FIXME:
  6. import AuthHandler from './modules/UI/authentication/AuthHandler';
  7. import ConnectionQuality from './modules/connectionquality/connectionquality';
  8. import CQEvents from './service/connectionquality/CQEvents';
  9. import UIEvents from './service/UI/UIEvents';
  10. import DSEvents from './service/desktopsharing/DesktopSharingEventTypes';
  11. const ConnectionEvents = JitsiMeetJS.events.connection;
  12. const ConnectionErrors = JitsiMeetJS.errors.connection;
  13. const ConferenceEvents = JitsiMeetJS.events.conference;
  14. const ConferenceErrors = JitsiMeetJS.errors.conference;
  15. const TrackEvents = JitsiMeetJS.events.track;
  16. let room, connection, localTracks, localAudio, localVideo, roomLocker;
  17. /**
  18. * Known custom conference commands.
  19. */
  20. const Commands = {
  21. CONNECTION_QUALITY: "stats",
  22. EMAIL: "email",
  23. ETHERPAD: "etherpad",
  24. PREZI: "prezi",
  25. STOP_PREZI: "stop-prezi"
  26. };
  27. /**
  28. * Open Connection. When authentication failed it shows auth dialog.
  29. * @returns Promise<JitsiConnection>
  30. */
  31. function connect() {
  32. return openConnection({retry: true}).catch(function (err) {
  33. if (err === ConnectionErrors.PASSWORD_REQUIRED) {
  34. APP.UI.notifyTokenAuthFailed();
  35. } else {
  36. APP.UI.notifyConnectionFailed(err);
  37. }
  38. throw err;
  39. });
  40. }
  41. /**
  42. * Add local track to the conference and shares
  43. * video type with other users if its video track.
  44. * @param {JitsiLocalTrack} track local track
  45. */
  46. function addTrack (track) {
  47. room.addTrack(track);
  48. if (track.isAudioTrack()) {
  49. return;
  50. }
  51. }
  52. /**
  53. * Share email with other users.
  54. * @param {string} email new email
  55. */
  56. function sendEmail (email) {
  57. room.sendCommand(Commands.EMAIL, {
  58. value: email,
  59. attributes: {
  60. id: room.myUserId()
  61. }
  62. });
  63. }
  64. /**
  65. * Get user nickname by user id.
  66. * @param {string} id user id
  67. * @returns {string?} user nickname or undefined if user is unknown.
  68. */
  69. function getDisplayName (id) {
  70. if (APP.conference.isLocalId(id)) {
  71. return APP.settings.getDisplayName();
  72. }
  73. let participant = room.getParticipantById(id);
  74. if (participant && participant.getDisplayName()) {
  75. return participant.getDisplayName();
  76. }
  77. }
  78. class ConferenceConnector {
  79. constructor(resolve, reject) {
  80. this._resolve = resolve;
  81. this._reject = reject;
  82. this.reconnectTimeout = null;
  83. room.on(ConferenceEvents.CONFERENCE_JOINED,
  84. this._handleConferenceJoined.bind(this));
  85. room.on(ConferenceEvents.CONFERENCE_FAILED,
  86. this._onConferenceFailed.bind(this));
  87. room.on(ConferenceEvents.CONFERENCE_ERROR,
  88. this._onConferenceError.bind(this));
  89. }
  90. _handleConferenceFailed(err, msg) {
  91. this._unsubscribe();
  92. this._reject(err);
  93. }
  94. _onConferenceFailed(err, ...params) {
  95. console.error('CONFERENCE FAILED:', err, ...params);
  96. switch (err) {
  97. // room is locked by the password
  98. case ConferenceErrors.PASSWORD_REQUIRED:
  99. APP.UI.markRoomLocked(true);
  100. roomLocker.requirePassword().then(function () {
  101. room.join(roomLocker.password);
  102. });
  103. break;
  104. case ConferenceErrors.CONNECTION_ERROR:
  105. {
  106. let [msg] = params;
  107. APP.UI.notifyConnectionFailed(msg);
  108. }
  109. break;
  110. case ConferenceErrors.VIDEOBRIDGE_NOT_AVAILABLE:
  111. APP.UI.notifyBridgeDown();
  112. break;
  113. // not enough rights to create conference
  114. case ConferenceErrors.AUTHENTICATION_REQUIRED:
  115. // schedule reconnect to check if someone else created the room
  116. this.reconnectTimeout = setTimeout(function () {
  117. room.join();
  118. }, 5000);
  119. // notify user that auth is required
  120. AuthHandler.requireAuth(APP.conference.roomName);
  121. break;
  122. case ConferenceErrors.RESERVATION_ERROR:
  123. {
  124. let [code, msg] = params;
  125. APP.UI.notifyReservationError(code, msg);
  126. }
  127. break;
  128. case ConferenceErrors.GRACEFUL_SHUTDOWN:
  129. APP.UI.notifyGracefulShudown();
  130. break;
  131. case ConferenceErrors.JINGLE_FATAL_ERROR:
  132. APP.UI.notifyInternalError();
  133. break;
  134. case ConferenceErrors.CONFERENCE_DESTROYED:
  135. {
  136. let [reason] = params;
  137. APP.UI.hideStats();
  138. APP.UI.notifyConferenceDestroyed(reason);
  139. }
  140. break;
  141. case ConferenceErrors.FOCUS_DISCONNECTED:
  142. {
  143. let [focus, retrySec] = params;
  144. APP.UI.notifyFocusDisconnected(focus, retrySec);
  145. }
  146. break;
  147. default:
  148. this._handleConferenceFailed(err, ...params);
  149. }
  150. }
  151. _onConferenceError(err, ...params) {
  152. console.error('CONFERENCE Error:', err, params);
  153. switch (err) {
  154. case ConferenceErrors.CHAT_ERROR:
  155. {
  156. let [code, msg] = params;
  157. APP.UI.showChatError(code, msg);
  158. }
  159. break;
  160. default:
  161. console.error("Unknown error.");
  162. }
  163. }
  164. _unsubscribe() {
  165. room.off(
  166. ConferenceEvents.CONFERENCE_JOINED, this._handleConferenceJoined);
  167. room.off(
  168. ConferenceEvents.CONFERENCE_FAILED, this._onConferenceFailed);
  169. if (this.reconnectTimeout !== null) {
  170. clearTimeout(this.reconnectTimeout);
  171. }
  172. AuthHandler.closeAuth();
  173. }
  174. _handleConferenceJoined() {
  175. this._unsubscribe();
  176. this._resolve();
  177. }
  178. connect() {
  179. room.join();
  180. }
  181. }
  182. export default {
  183. localId: undefined,
  184. isModerator: false,
  185. audioMuted: false,
  186. videoMuted: false,
  187. /**
  188. * Open new connection and join to the conference.
  189. * @param {object} options
  190. * @param {string} roomName name of the conference
  191. * @returns {Promise}
  192. */
  193. init(options) {
  194. this.roomName = options.roomName;
  195. JitsiMeetJS.setLogLevel(JitsiMeetJS.logLevels.TRACE);
  196. return JitsiMeetJS.init(config).then(() => {
  197. return Promise.all([
  198. this.createLocalTracks('audio', 'video').catch(()=>{
  199. return this.createLocalTracks('audio');
  200. }).catch(
  201. () => {return [];}),
  202. connect()
  203. ]);
  204. }).then(([tracks, con]) => {
  205. console.log('initialized with %s local tracks', tracks.length);
  206. localTracks = tracks;
  207. connection = con;
  208. this._createRoom();
  209. // XXX The API will take care of disconnecting from the XMPP server
  210. // (and, thus, leaving the room) on unload.
  211. return new Promise((resolve, reject) => {
  212. (new ConferenceConnector(resolve, reject)).connect();
  213. });
  214. });
  215. },
  216. /**
  217. * Create local tracks of specified types.
  218. * If we cannot obtain required tracks it will return empty array.
  219. * @param {string[]} devices required track types ('audio', 'video' etc.)
  220. * @returns {Promise<JitsiLocalTrack[]>}
  221. */
  222. createLocalTracks (...devices) {
  223. return JitsiMeetJS.createLocalTracks({
  224. // copy array to avoid mutations inside library
  225. devices: devices.slice(0),
  226. resolution: config.resolution,
  227. // adds any ff fake device settings if any
  228. firefox_fake_device: config.firefox_fake_device
  229. }).catch(function (err) {
  230. console.error('failed to create local tracks', ...devices, err);
  231. return Promise.reject(err);
  232. });
  233. },
  234. /**
  235. * Check if id is id of the local user.
  236. * @param {string} id id to check
  237. * @returns {boolean}
  238. */
  239. isLocalId (id) {
  240. return this.localId === id;
  241. },
  242. /**
  243. * Simulates toolbar button click for audio mute. Used by shortcuts and API.
  244. * @param mute true for mute and false for unmute.
  245. */
  246. muteAudio (mute) {
  247. //FIXME: Maybe we should create method for that in the UI instead of
  248. //accessing directly eventEmitter????
  249. APP.UI.eventEmitter.emit(UIEvents.AUDIO_MUTED, mute);
  250. },
  251. /**
  252. * Simulates toolbar button click for audio mute. Used by shortcuts and API.
  253. */
  254. toggleAudioMuted () {
  255. this.muteAudio(!this.audioMuted);
  256. },
  257. /**
  258. * Simulates toolbar button click for video mute. Used by shortcuts and API.
  259. * @param mute true for mute and false for unmute.
  260. */
  261. muteVideo (mute) {
  262. //FIXME: Maybe we should create method for that in the UI instead of
  263. //accessing directly eventEmitter????
  264. APP.UI.eventEmitter.emit(UIEvents.VIDEO_MUTED, mute);
  265. },
  266. /**
  267. * Simulates toolbar button click for video mute. Used by shortcuts and API.
  268. */
  269. toggleVideoMuted () {
  270. this.muteVideo(!this.videoMuted);
  271. },
  272. /**
  273. * Retrieve list of conference participants (without local user).
  274. * @returns {JitsiParticipant[]}
  275. */
  276. listMembers () {
  277. return room.getParticipants();
  278. },
  279. /**
  280. * Retrieve list of ids of conference participants (without local user).
  281. * @returns {string[]}
  282. */
  283. listMembersIds () {
  284. return room.getParticipants().map(p => p.getId());
  285. },
  286. /**
  287. * Check if SIP is supported.
  288. * @returns {boolean}
  289. */
  290. sipGatewayEnabled () {
  291. return room.isSIPCallingSupported();
  292. },
  293. get membersCount () {
  294. return room.getParticipants().length + 1;
  295. },
  296. get startAudioMuted () {
  297. return room && room.getStartMutedPolicy().audio;
  298. },
  299. get startVideoMuted () {
  300. return room && room.getStartMutedPolicy().video;
  301. },
  302. /**
  303. * Returns true if the callstats integration is enabled, otherwise returns
  304. * false.
  305. *
  306. * @returns true if the callstats integration is enabled, otherwise returns
  307. * false.
  308. */
  309. isCallstatsEnabled () {
  310. return room.isCallstatsEnabled();
  311. },
  312. /**
  313. * Sends the given feedback through CallStats if enabled.
  314. *
  315. * @param overallFeedback an integer between 1 and 5 indicating the
  316. * user feedback
  317. * @param detailedFeedback detailed feedback from the user. Not yet used
  318. */
  319. sendFeedback (overallFeedback, detailedFeedback) {
  320. return room.sendFeedback (overallFeedback, detailedFeedback);
  321. },
  322. // used by torture currently
  323. isJoined () {
  324. return this._room
  325. && this._room.isJoined();
  326. },
  327. getConnectionState () {
  328. return this._room
  329. && this._room.getConnectionState();
  330. },
  331. getMyUserId () {
  332. return this._room
  333. && this._room.myUserId();
  334. },
  335. /**
  336. * Will be filled with values only when config.debug is enabled.
  337. * Its used by torture to check audio levels.
  338. */
  339. audioLevelsMap: {},
  340. /**
  341. * Returns the stored audio level (stored only if config.debug is enabled)
  342. * @param id the id for the user audio level to return (the id value is
  343. * returned for the participant using getMyUserId() method)
  344. */
  345. getPeerSSRCAudioLevel (id) {
  346. return this.audioLevelsMap[id];
  347. },
  348. /**
  349. * Will check for number of remote particiapnts that have at least one
  350. * remote track.
  351. * @return {boolean} whether we have enough participants with remote streams
  352. */
  353. checkEnoughParticipants (number) {
  354. var participants = this._room.getParticipants();
  355. var foundParticipants = 0;
  356. for (var i = 0; i < participants.length; i += 1) {
  357. if (participants[i].getTracks().length > 0) {
  358. foundParticipants++;
  359. }
  360. }
  361. return foundParticipants >= number;
  362. },
  363. /**
  364. * Returns the stats.
  365. */
  366. getStats() {
  367. return ConnectionQuality.getStats();
  368. },
  369. // end used by torture
  370. getLogs () {
  371. return room.getLogs();
  372. },
  373. _createRoom () {
  374. room = connection.initJitsiConference(APP.conference.roomName,
  375. this._getConferenceOptions());
  376. this.localId = room.myUserId();
  377. localTracks.forEach((track) => {
  378. if(track.isAudioTrack()) {
  379. localAudio = track;
  380. }
  381. else if (track.isVideoTrack()) {
  382. localVideo = track;
  383. }
  384. addTrack(track);
  385. APP.UI.addLocalStream(track);
  386. });
  387. roomLocker = createRoomLocker(room);
  388. this._room = room; // FIXME do not use this
  389. this.localId = room.myUserId();
  390. let email = APP.settings.getEmail();
  391. email && sendEmail(email);
  392. let nick = APP.settings.getDisplayName();
  393. (config.useNicks && !nick) && (() => {
  394. nick = APP.UI.askForNickname();
  395. APP.settings.setDisplayName(nick);
  396. })();
  397. nick && room.setDisplayName(nick);
  398. this._setupListeners();
  399. },
  400. _getConferenceOptions() {
  401. let options = config;
  402. if(config.enableRecording) {
  403. options.recordingType = (config.hosts &&
  404. (typeof config.hosts.jirecon != "undefined"))?
  405. "jirecon" : "colibri";
  406. }
  407. return options;
  408. },
  409. /**
  410. * Setup interaction between conference and UI.
  411. */
  412. _setupListeners () {
  413. // add local streams when joined to the conference
  414. room.on(ConferenceEvents.CONFERENCE_JOINED, () => {
  415. APP.UI.updateAuthInfo(room.isAuthEnabled(), room.getAuthLogin());
  416. APP.UI.mucJoined();
  417. });
  418. room.on(ConferenceEvents.USER_JOINED, (id, user) => {
  419. console.log('USER %s connnected', id, user);
  420. APP.API.notifyUserJoined(id);
  421. // FIXME email???
  422. APP.UI.addUser(id, user.getDisplayName());
  423. // chek the roles for the new user and reflect them
  424. APP.UI.updateUserRole(user);
  425. });
  426. room.on(ConferenceEvents.USER_LEFT, (id, user) => {
  427. console.log('USER %s LEFT', id, user);
  428. APP.API.notifyUserLeft(id);
  429. APP.UI.removeUser(id, user.getDisplayName());
  430. APP.UI.stopPrezi(id);
  431. });
  432. room.on(ConferenceEvents.USER_ROLE_CHANGED, (id, role) => {
  433. if (this.isLocalId(id)) {
  434. console.info(`My role changed, new role: ${role}`);
  435. this.isModerator = room.isModerator();
  436. APP.UI.updateLocalRole(room.isModerator());
  437. } else {
  438. let user = room.getParticipantById(id);
  439. if (user) {
  440. APP.UI.updateUserRole(user);
  441. }
  442. }
  443. });
  444. room.on(ConferenceEvents.TRACK_ADDED, (track) => {
  445. if(!track || track.isLocal())
  446. return;
  447. track.on(TrackEvents.TRACK_VIDEOTYPE_CHANGED, (type) => {
  448. APP.UI.onPeerVideoTypeChanged(track.getParticipantId(), type);
  449. });
  450. APP.UI.addRemoteStream(track);
  451. });
  452. room.on(ConferenceEvents.TRACK_REMOVED, (track) => {
  453. // FIXME handle
  454. });
  455. room.on(ConferenceEvents.TRACK_MUTE_CHANGED, (track) => {
  456. if(!track)
  457. return;
  458. const handler = (track.getType() === "audio")?
  459. APP.UI.setAudioMuted : APP.UI.setVideoMuted;
  460. let id;
  461. const mute = track.isMuted();
  462. if(track.isLocal()){
  463. id = this.localId;
  464. if(track.getType() === "audio") {
  465. this.audioMuted = mute;
  466. } else {
  467. this.videoMuted = mute;
  468. }
  469. } else {
  470. id = track.getParticipantId();
  471. }
  472. handler(id , mute);
  473. });
  474. room.on(ConferenceEvents.TRACK_AUDIO_LEVEL_CHANGED, (id, lvl) => {
  475. if(this.isLocalId(id) && localAudio && localAudio.isMuted()) {
  476. lvl = 0;
  477. }
  478. if(config.debug)
  479. this.audioLevelsMap[id] = lvl;
  480. APP.UI.setAudioLevel(id, lvl);
  481. });
  482. room.on(ConferenceEvents.IN_LAST_N_CHANGED, (inLastN) => {
  483. //FIXME
  484. if (config.muteLocalVideoIfNotInLastN) {
  485. // TODO mute or unmute if required
  486. // mark video on UI
  487. // APP.UI.markVideoMuted(true/false);
  488. }
  489. });
  490. room.on(
  491. ConferenceEvents.LAST_N_ENDPOINTS_CHANGED, (ids, enteringIds) => {
  492. APP.UI.handleLastNEndpoints(ids, enteringIds);
  493. });
  494. room.on(ConferenceEvents.DOMINANT_SPEAKER_CHANGED, (id) => {
  495. APP.UI.markDominantSpeaker(id);
  496. });
  497. if (!interfaceConfig.filmStripOnly) {
  498. room.on(ConferenceEvents.CONNECTION_INTERRUPTED, () => {
  499. APP.UI.markVideoInterrupted(true);
  500. });
  501. room.on(ConferenceEvents.CONNECTION_RESTORED, () => {
  502. APP.UI.markVideoInterrupted(false);
  503. });
  504. room.on(ConferenceEvents.MESSAGE_RECEIVED, (id, text, ts) => {
  505. let nick = getDisplayName(id);
  506. APP.API.notifyReceivedChatMessage(id, nick, text, ts);
  507. APP.UI.addMessage(id, nick, text, ts);
  508. });
  509. }
  510. room.on(ConferenceEvents.DISPLAY_NAME_CHANGED, (id, displayName) => {
  511. APP.API.notifyDisplayNameChanged(id, displayName);
  512. APP.UI.changeDisplayName(id, displayName);
  513. });
  514. room.on(ConferenceEvents.RECORDING_STATE_CHANGED, (status, error) => {
  515. if(status == "error") {
  516. console.error(error);
  517. return;
  518. }
  519. APP.UI.updateRecordingState(status);
  520. });
  521. room.on(ConferenceEvents.USER_STATUS_CHANGED, function (id, status) {
  522. APP.UI.updateUserStatus(id, status);
  523. });
  524. room.on(ConferenceEvents.KICKED, () => {
  525. APP.UI.hideStats();
  526. APP.UI.notifyKicked();
  527. // FIXME close
  528. });
  529. room.on(ConferenceEvents.DTMF_SUPPORT_CHANGED, (isDTMFSupported) => {
  530. APP.UI.updateDTMFSupport(isDTMFSupported);
  531. });
  532. room.on(ConferenceEvents.FIREFOX_EXTENSION_NEEDED, function (url) {
  533. APP.UI.notifyFirefoxExtensionRequired(url);
  534. });
  535. APP.UI.addListener(UIEvents.ROOM_LOCK_CLICKED, () => {
  536. if (room.isModerator()) {
  537. let promise = roomLocker.isLocked
  538. ? roomLocker.askToUnlock()
  539. : roomLocker.askToLock();
  540. promise.then(() => {
  541. APP.UI.markRoomLocked(roomLocker.isLocked);
  542. });
  543. } else {
  544. roomLocker.notifyModeratorRequired();
  545. }
  546. });
  547. APP.UI.addListener(UIEvents.AUDIO_MUTED, (muted) => {
  548. if(!localAudio)
  549. return;
  550. (muted)? localAudio.mute() : localAudio.unmute();
  551. });
  552. APP.UI.addListener(UIEvents.VIDEO_MUTED, (muted) => {
  553. if(!localVideo)
  554. return;
  555. (muted)? localVideo.mute() : localVideo.unmute();
  556. });
  557. if (!interfaceConfig.filmStripOnly) {
  558. APP.UI.addListener(UIEvents.MESSAGE_CREATED, (message) => {
  559. APP.API.notifySendingChatMessage(message);
  560. room.sendTextMessage(message);
  561. });
  562. }
  563. room.on(ConferenceEvents.CONNECTION_STATS, function (stats) {
  564. ConnectionQuality.updateLocalStats(stats);
  565. });
  566. ConnectionQuality.addListener(
  567. CQEvents.LOCALSTATS_UPDATED,
  568. (percent, stats) => {
  569. APP.UI.updateLocalStats(percent, stats);
  570. // send local stats to other users
  571. room.sendCommandOnce(Commands.CONNECTION_QUALITY, {
  572. children: ConnectionQuality.convertToMUCStats(stats),
  573. attributes: {
  574. xmlns: 'http://jitsi.org/jitmeet/stats'
  575. }
  576. });
  577. }
  578. );
  579. // listen to remote stats
  580. room.addCommandListener(Commands.CONNECTION_QUALITY,(values, from) => {
  581. ConnectionQuality.updateRemoteStats(from, values);
  582. });
  583. ConnectionQuality.addListener(CQEvents.REMOTESTATS_UPDATED,
  584. (id, percent, stats) => {
  585. APP.UI.updateRemoteStats(id, percent, stats);
  586. });
  587. room.addCommandListener(Commands.ETHERPAD, ({value}) => {
  588. APP.UI.initEtherpad(value);
  589. });
  590. room.addCommandListener(Commands.PREZI, ({value, attributes}) => {
  591. APP.UI.showPrezi(attributes.id, value, attributes.slide);
  592. });
  593. room.addCommandListener(Commands.STOP_PREZI, ({attributes}) => {
  594. APP.UI.stopPrezi(attributes.id);
  595. });
  596. APP.UI.addListener(UIEvents.SHARE_PREZI, (url, slide) => {
  597. console.log('Sharing Prezi %s slide %s', url, slide);
  598. room.removeCommand(Commands.PREZI);
  599. room.sendCommand(Commands.PREZI, {
  600. value: url,
  601. attributes: {
  602. id: room.myUserId(),
  603. slide
  604. }
  605. });
  606. });
  607. APP.UI.addListener(UIEvents.STOP_SHARING_PREZI, () => {
  608. room.removeCommand(Commands.PREZI);
  609. room.sendCommandOnce(Commands.STOP_PREZI, {
  610. attributes: {
  611. id: room.myUserId()
  612. }
  613. });
  614. });
  615. APP.UI.addListener(UIEvents.EMAIL_CHANGED, (email) => {
  616. APP.settings.setEmail(email);
  617. APP.UI.setUserAvatar(room.myUserId(), email);
  618. sendEmail(email);
  619. });
  620. room.addCommandListener(Commands.EMAIL, (data) => {
  621. APP.UI.setUserAvatar(data.attributes.id, data.value);
  622. });
  623. APP.UI.addListener(UIEvents.NICKNAME_CHANGED, (nickname) => {
  624. APP.settings.setDisplayName(nickname);
  625. room.setDisplayName(nickname);
  626. APP.UI.changeDisplayName(APP.conference.localId, nickname);
  627. });
  628. APP.UI.addListener(UIEvents.START_MUTED_CHANGED,
  629. (startAudioMuted, startVideoMuted) => {
  630. room.setStartMutedPolicy({audio: startAudioMuted,
  631. video: startVideoMuted});
  632. }
  633. );
  634. room.on(
  635. ConferenceEvents.START_MUTED_POLICY_CHANGED,
  636. (policy) => {
  637. APP.UI.onStartMutedChanged();
  638. }
  639. );
  640. room.on(ConferenceEvents.STARTED_MUTED, () => {
  641. (room.isStartAudioMuted() || room.isStartVideoMuted())
  642. && APP.UI.notifyInitiallyMuted();
  643. });
  644. APP.UI.addListener(UIEvents.USER_INVITED, (roomUrl) => {
  645. APP.UI.inviteParticipants(
  646. roomUrl,
  647. APP.conference.roomName,
  648. roomLocker.password,
  649. APP.settings.getDisplayName()
  650. );
  651. });
  652. room.on(
  653. ConferenceEvents.AVAILABLE_DEVICES_CHANGED, function (id, devices) {
  654. APP.UI.updateDevicesAvailability(id, devices);
  655. }
  656. );
  657. // call hangup
  658. APP.UI.addListener(UIEvents.HANGUP, () => {
  659. APP.UI.requestFeedback().then(() => {
  660. connection.disconnect();
  661. config.enableWelcomePage && setTimeout(() => {
  662. window.localStorage.welcomePageDisabled = false;
  663. window.location.pathname = "/";
  664. }, 3000);
  665. }, (err) => {console.error(err);});
  666. });
  667. // logout
  668. APP.UI.addListener(UIEvents.LOGOUT, () => {
  669. // FIXME handle logout
  670. // APP.xmpp.logout(function (url) {
  671. // if (url) {
  672. // window.location.href = url;
  673. // } else {
  674. // hangup();
  675. // }
  676. // });
  677. });
  678. APP.UI.addListener(UIEvents.SIP_DIAL, (sipNumber) => {
  679. room.dial(sipNumber);
  680. });
  681. // Starts or stops the recording for the conference.
  682. APP.UI.addListener(UIEvents.RECORDING_TOGGLE, (predefinedToken) => {
  683. if (predefinedToken) {
  684. room.toggleRecording({token: predefinedToken});
  685. return;
  686. }
  687. APP.UI.requestRecordingToken().then((token) => {
  688. room.toggleRecording({token: token});
  689. });
  690. });
  691. APP.UI.addListener(UIEvents.SUBJECT_CHANGED, (topic) => {
  692. room.setSubject(topic);
  693. });
  694. room.on(ConferenceEvents.SUBJECT_CHANGED, function (subject) {
  695. APP.UI.setSubject(subject);
  696. });
  697. APP.UI.addListener(UIEvents.USER_KICKED, (id) => {
  698. room.kickParticipant(id);
  699. });
  700. APP.UI.addListener(UIEvents.REMOTE_AUDIO_MUTED, (id) => {
  701. room.muteParticipant(id);
  702. });
  703. APP.UI.addListener(UIEvents.AUTH_CLICKED, () => {
  704. AuthHandler.authenticate(room);
  705. });
  706. APP.UI.addListener(UIEvents.SELECTED_ENDPOINT, (id) => {
  707. room.selectParticipant(id);
  708. });
  709. APP.UI.addListener(UIEvents.PINNED_ENDPOINT, (id) => {
  710. room.pinParticipant(id);
  711. });
  712. APP.UI.addListener(UIEvents.TOGGLE_SCREENSHARING, () => {
  713. APP.desktopsharing.toggleScreenSharing();
  714. });
  715. APP.desktopsharing.addListener(DSEvents.SWITCHING_DONE,
  716. (isSharingScreen) => {
  717. APP.UI.updateDesktopSharingButtons(isSharingScreen);
  718. });
  719. APP.desktopsharing.addListener(DSEvents.FIREFOX_EXTENSION_NEEDED,
  720. (url) => {
  721. APP.UI.showExtensionRequiredDialog(url);
  722. });
  723. APP.desktopsharing.addListener(DSEvents.NEW_STREAM_CREATED,
  724. (track, callback) => {
  725. const localCallback = (newTrack) => {
  726. if(!newTrack || !localVideo || !newTrack.isLocal() ||
  727. newTrack !== localVideo)
  728. return;
  729. if(localVideo.isMuted() &&
  730. localVideo.videoType !== track.videoType) {
  731. localVideo.mute();
  732. }
  733. callback();
  734. if(room)
  735. room.off(ConferenceEvents.TRACK_ADDED, localCallback);
  736. };
  737. if(room) {
  738. room.on(ConferenceEvents.TRACK_ADDED, localCallback);
  739. }
  740. if(localVideo)
  741. localVideo.stop();
  742. localVideo = track;
  743. addTrack(track);
  744. if(!room)
  745. localCallback();
  746. APP.UI.addLocalStream(track);
  747. }
  748. );
  749. }
  750. };