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.3KB

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