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.

DeviceSelectionPopup.js 8.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. import Logger from 'jitsi-meet-logger';
  2. import React from 'react';
  3. import ReactDOM from 'react-dom';
  4. import { I18nextProvider } from 'react-i18next';
  5. import {
  6. PostMessageTransportBackend,
  7. Transport
  8. } from '../../../modules/transport';
  9. import { parseURLParams } from '../base/config';
  10. import DeviceSelectionDialogBase from './components/DeviceSelectionDialogBase';
  11. declare var JitsiMeetJS: Object;
  12. const logger = Logger.getLogger(__filename);
  13. /**
  14. * Implements a class that renders the React components for the device selection
  15. * popup page and handles the communication between the components and Jitsi
  16. * Meet.
  17. */
  18. export default class DeviceSelectionPopup {
  19. /**
  20. * Initializes a new DeviceSelectionPopup instance.
  21. *
  22. * @param {Object} i18next - The i18next instance used for translation.
  23. */
  24. constructor(i18next) {
  25. this.close = this.close.bind(this);
  26. this._setVideoInputDevice = this._setVideoInputDevice.bind(this);
  27. this._setAudioInputDevice = this._setAudioInputDevice.bind(this);
  28. this._setAudioOutputDevice = this._setAudioOutputDevice.bind(this);
  29. this._i18next = i18next;
  30. const { scope } = parseURLParams(window.location);
  31. this._transport = new Transport({
  32. backend: new PostMessageTransportBackend({
  33. postisOptions: {
  34. scope,
  35. window: window.opener
  36. }
  37. })
  38. });
  39. this._transport.on('event', event => {
  40. if (event.name === 'deviceListChanged') {
  41. this._updateAvailableDevices();
  42. return true;
  43. }
  44. return false;
  45. });
  46. this._dialogProps = {
  47. availableDevices: {},
  48. currentAudioInputId: '',
  49. currentAudioOutputId: '',
  50. currentVideoInputId: '',
  51. disableAudioInputChange: true,
  52. disableDeviceChange: true,
  53. hasAudioPermission: JitsiMeetJS.mediaDevices
  54. .isDevicePermissionGranted.bind(null, 'audio'),
  55. hasVideoPermission: JitsiMeetJS.mediaDevices
  56. .isDevicePermissionGranted.bind(null, 'video'),
  57. hideAudioInputPreview: !JitsiMeetJS.isCollectingLocalStats(),
  58. hideAudioOutputSelect: true
  59. };
  60. this._initState();
  61. }
  62. /**
  63. * Sends event to Jitsi Meet to close the popup dialog.
  64. *
  65. * @returns {void}
  66. */
  67. close() {
  68. this._transport.sendEvent({
  69. type: 'devices-dialog',
  70. name: 'close'
  71. });
  72. }
  73. /**
  74. * Changes the properties of the react component and re-renders it.
  75. *
  76. * @param {Object} newProps - The new properties that will be assigned to
  77. * the current ones.
  78. * @returns {void}
  79. */
  80. _changeDialogProps(newProps) {
  81. this._dialogProps = {
  82. ...this._dialogProps,
  83. ...newProps
  84. };
  85. this._render();
  86. }
  87. /**
  88. * Returns Promise that resolves with result an list of available devices.
  89. *
  90. * @returns {Promise}
  91. */
  92. _getAvailableDevices() {
  93. return this._transport.sendRequest({
  94. type: 'devices',
  95. name: 'getAvailableDevices'
  96. }).catch(e => {
  97. logger.error(e);
  98. return {};
  99. });
  100. }
  101. /**
  102. * Returns Promise that resolves with current selected devices.
  103. *
  104. * @returns {Promise}
  105. */
  106. _getCurrentDevices() {
  107. return this._transport.sendRequest({
  108. type: 'devices',
  109. name: 'getCurrentDevices'
  110. }).catch(e => {
  111. logger.error(e);
  112. return {};
  113. });
  114. }
  115. /**
  116. * Initializes the state.
  117. *
  118. * @returns {void}
  119. */
  120. _initState() {
  121. return Promise.all([
  122. this._getAvailableDevices(),
  123. this._isDeviceListAvailable(),
  124. this._isDeviceChangeAvailable(),
  125. this._isDeviceChangeAvailable('output'),
  126. this._getCurrentDevices(),
  127. this._isMultipleAudioInputSupported()
  128. ]).then(([
  129. availableDevices,
  130. listAvailable,
  131. changeAvailable,
  132. changeOutputAvailable,
  133. currentDevices,
  134. multiAudioInputSupported
  135. ]) => {
  136. this._changeDialogProps({
  137. availableDevices,
  138. currentAudioInputId: currentDevices.audioInput,
  139. currentAudioOutputId: currentDevices.audioOutput,
  140. currentVideoInputId: currentDevices.videoInput,
  141. disableAudioInputChange: !multiAudioInputSupported,
  142. disableDeviceChange: !listAvailable || !changeAvailable,
  143. hideAudioOutputSelect: !changeOutputAvailable
  144. });
  145. });
  146. }
  147. /**
  148. * Returns Promise that resolves with true if the device change is available
  149. * and with false if not.
  150. *
  151. * @param {string} [deviceType] - Values - 'output', 'input' or undefined.
  152. * Default - 'input'.
  153. * @returns {Promise}
  154. */
  155. _isDeviceChangeAvailable(deviceType) {
  156. return this._transport.sendRequest({
  157. deviceType,
  158. type: 'devices',
  159. name: 'isDeviceChangeAvailable'
  160. }).catch(e => {
  161. logger.error(e);
  162. return false;
  163. });
  164. }
  165. /**
  166. * Returns Promise that resolves with true if the device list is available
  167. * and with false if not.
  168. *
  169. * @returns {Promise}
  170. */
  171. _isDeviceListAvailable() {
  172. return this._transport.sendRequest({
  173. type: 'devices',
  174. name: 'isDeviceListAvailable'
  175. }).catch(e => {
  176. logger.error(e);
  177. return false;
  178. });
  179. }
  180. /**
  181. * Returns Promise that resolves with true if the device list is available
  182. * and with false if not.
  183. *
  184. * @returns {Promise}
  185. */
  186. _isMultipleAudioInputSupported() {
  187. return this._transport.sendRequest({
  188. type: 'devices',
  189. name: 'isMultipleAudioInputSupported'
  190. }).catch(e => {
  191. logger.error(e);
  192. return false;
  193. });
  194. }
  195. /**
  196. * Renders the React components for the popup page.
  197. *
  198. * @returns {void}
  199. */
  200. _render() {
  201. const props = {
  202. ...this._dialogProps,
  203. closeModal: this.close,
  204. disableBlanketClickDismiss: true,
  205. setAudioInputDevice: this._setAudioInputDevice,
  206. setAudioOutputDevice: this._setAudioOutputDevice,
  207. setVideoInputDevice: this._setVideoInputDevice
  208. };
  209. ReactDOM.render(
  210. <I18nextProvider
  211. i18n = { this._i18next }>
  212. <DeviceSelectionDialogBase { ...props } />
  213. </I18nextProvider>,
  214. document.getElementById('react'));
  215. }
  216. /**
  217. * Sets the audio input device to the one with the id that is passed.
  218. *
  219. * @param {string} id - The id of the new device.
  220. * @returns {Promise}
  221. */
  222. _setAudioInputDevice(id) {
  223. return this._setDevice({
  224. id,
  225. kind: 'audioinput'
  226. });
  227. }
  228. /**
  229. * Sets the audio output device to the one with the id that is passed.
  230. *
  231. * @param {string} id - The id of the new device.
  232. * @returns {Promise}
  233. */
  234. _setAudioOutputDevice(id) {
  235. return this._setDevice({
  236. id,
  237. kind: 'audiooutput'
  238. });
  239. }
  240. /**
  241. * Sets the currently used device to the one that is passed.
  242. *
  243. * @param {Object} device - The new device to be used.
  244. * @returns {Promise}
  245. */
  246. _setDevice(device) {
  247. return this._transport.sendRequest({
  248. type: 'devices',
  249. name: 'setDevice',
  250. device
  251. });
  252. }
  253. /**
  254. * Sets the video input device to the one with the id that is passed.
  255. *
  256. * @param {string} id - The id of the new device.
  257. * @returns {Promise}
  258. */
  259. _setVideoInputDevice(id) {
  260. return this._setDevice({
  261. id,
  262. kind: 'videoinput'
  263. });
  264. }
  265. /**
  266. * Updates the available devices.
  267. *
  268. * @returns {void}
  269. */
  270. _updateAvailableDevices() {
  271. this._getAvailableDevices().then(devices =>
  272. this._changeDialogProps({ availableDevices: devices })
  273. );
  274. }
  275. }