Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

SettingsMenu.js 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. /* global $, APP, AJS, interfaceConfig, JitsiMeetJS */
  2. import { LANGUAGES } from "../../../../react/features/base/i18n";
  3. import UIUtil from "../../util/UIUtil";
  4. import UIEvents from "../../../../service/UI/UIEvents";
  5. import Settings from '../../../settings/Settings';
  6. const sidePanelsContainerId = 'sideToolbarContainer';
  7. const htmlStr = `
  8. <div id="settings_container" class="sideToolbarContainer__inner">
  9. <div class="title" data-i18n="settings.title"></div>
  10. <form class="aui">
  11. <div id="languagesSelectWrapper"
  12. class="sideToolbarBlock first hide">
  13. <select id="languagesSelect"></select>
  14. </div>
  15. <div id="deviceOptionsWrapper" class="hide">
  16. <div id="deviceOptionsTitle" class="subTitle hide"
  17. data-i18n="settings.audioVideo"></div>
  18. <div class="sideToolbarBlock first">
  19. <label class="first" data-i18n="settings.selectCamera">
  20. </label>
  21. <select id="selectCamera"></select>
  22. </div>
  23. <div class="sideToolbarBlock">
  24. <label data-i18n="settings.selectMic"></label>
  25. <select id="selectMic"></select>
  26. </div>
  27. <div class="sideToolbarBlock">
  28. <label data-i18n="settings.selectAudioOutput"></label>
  29. <select id="selectAudioOutput"></select>
  30. </div>
  31. </div>
  32. <div id="moderatorOptionsWrapper" class="hide">
  33. <div id="moderatorOptionsTitle" class="subTitle hide"
  34. data-i18n="settings.moderator"></div>
  35. <div id="startMutedOptions" class="hide">
  36. <div class="sideToolbarBlock first">
  37. <input type="checkbox" id="startAudioMuted">
  38. <label class="startMutedLabel" for="startAudioMuted"
  39. data-i18n="settings.startAudioMuted"></label>
  40. </div>
  41. <div class="sideToolbarBlock">
  42. <input type="checkbox" id="startVideoMuted">
  43. <label class="startMutedLabel" for="startVideoMuted"
  44. data-i18n="settings.startVideoMuted"></label>
  45. </div>
  46. </div>
  47. <div id="followMeOptions" class="hide">
  48. <div class="sideToolbarBlock">
  49. <input type="checkbox" id="followMeCheckBox">
  50. <label class="followMeLabel" for="followMeCheckBox"
  51. data-i18n="settings.followMe"></label>
  52. </div>
  53. </div>
  54. </div>
  55. </form>
  56. </div>`;
  57. function initHTML() {
  58. $(`#${sidePanelsContainerId}`)
  59. .append(htmlStr);
  60. }
  61. /**
  62. * Generate html select options for available languages.
  63. *
  64. * @param {string[]} items available languages
  65. * @param {string} [currentLang] current language
  66. * @returns {string}
  67. */
  68. function generateLanguagesOptions(items, currentLang) {
  69. return items.map(function (lang) {
  70. let attrs = {
  71. value: lang,
  72. 'data-i18n': `languages:${lang}`
  73. };
  74. if (lang === currentLang) {
  75. attrs.selected = 'selected';
  76. }
  77. let attrsStr = UIUtil.attrsToString(attrs);
  78. return `<option ${attrsStr}></option>`;
  79. }).join('');
  80. }
  81. /**
  82. * Generate html select options for available physical devices.
  83. *
  84. * @param {{ deviceId, label }[]} items available devices
  85. * @param {string} [selectedId] id of selected device
  86. * @param {boolean} permissionGranted if permission to use selected device type
  87. * is granted
  88. * @returns {string}
  89. */
  90. function generateDevicesOptions(items, selectedId, permissionGranted) {
  91. if (!permissionGranted && items.length) {
  92. return '<option data-i18n="settings.noPermission"></option>';
  93. }
  94. var options = items.map(function (item) {
  95. let attrs = {
  96. value: item.deviceId
  97. };
  98. if (item.deviceId === selectedId) {
  99. attrs.selected = 'selected';
  100. }
  101. let attrsStr = UIUtil.attrsToString(attrs);
  102. return `<option ${attrsStr}>${item.label}</option>`;
  103. });
  104. if (!items.length) {
  105. options.unshift('<option data-i18n="settings.noDevice"></option>');
  106. }
  107. return options.join('');
  108. }
  109. /**
  110. * Replace html select element to select2 custom dropdown
  111. *
  112. * @param {jQueryElement} $el native select element
  113. * @param {function} onSelectedCb fired if item is selected
  114. */
  115. function initSelect2($el, onSelectedCb) {
  116. $el.auiSelect2({
  117. minimumResultsForSearch: Infinity
  118. });
  119. if (typeof onSelectedCb === 'function') {
  120. $el.change(onSelectedCb);
  121. }
  122. }
  123. export default {
  124. init (emitter) {
  125. initHTML();
  126. //LANGUAGES BOX
  127. if (UIUtil.isSettingEnabled('language')) {
  128. const wrapperId = 'languagesSelectWrapper';
  129. const selectId = 'languagesSelect';
  130. const selectEl = AJS.$(`#${selectId}`);
  131. let selectInput;
  132. selectEl.html(generateLanguagesOptions(
  133. LANGUAGES,
  134. APP.translation.getCurrentLanguage()
  135. ));
  136. initSelect2(selectEl, () => {
  137. const val = selectEl.val();
  138. selectInput[0].dataset.i18n = `languages:${val}`;
  139. APP.translation.translateElement(selectInput);
  140. emitter.emit(UIEvents.LANG_CHANGED, val);
  141. });
  142. //find new selectInput element
  143. selectInput = $(`#s2id_${selectId} .select2-chosen`);
  144. //first select fix for languages options
  145. selectInput[0].dataset.i18n =
  146. `languages:${APP.translation.getCurrentLanguage()}`;
  147. APP.translation.translateElement(selectEl);
  148. APP.translation.addLanguageChangedListener(
  149. lng => selectInput[0].dataset.i18n = `languages:${lng}`);
  150. UIUtil.setVisible(wrapperId, true);
  151. }
  152. // DEVICES LIST
  153. if (UIUtil.isSettingEnabled('devices')) {
  154. const wrapperId = 'deviceOptionsWrapper';
  155. JitsiMeetJS.mediaDevices.isDeviceListAvailable()
  156. .then((isDeviceListAvailable) => {
  157. if (isDeviceListAvailable &&
  158. JitsiMeetJS.mediaDevices.isDeviceChangeAvailable()) {
  159. this._initializeDeviceSelectionSettings(emitter);
  160. }
  161. });
  162. // Only show the subtitle if this isn't the only setting section.
  163. if (interfaceConfig.SETTINGS_SECTIONS.length > 1)
  164. UIUtil.setVisible("deviceOptionsTitle", true);
  165. UIUtil.setVisible(wrapperId, true);
  166. }
  167. // MODERATOR
  168. if (UIUtil.isSettingEnabled('moderator')) {
  169. const wrapperId = 'moderatorOptionsWrapper';
  170. // START MUTED
  171. $("#startMutedOptions").change(function () {
  172. let startAudioMuted = $("#startAudioMuted").is(":checked");
  173. let startVideoMuted = $("#startVideoMuted").is(":checked");
  174. emitter.emit(
  175. UIEvents.START_MUTED_CHANGED,
  176. startAudioMuted,
  177. startVideoMuted
  178. );
  179. });
  180. // FOLLOW ME
  181. const followMeToggle = document.getElementById('followMeCheckBox');
  182. followMeToggle.addEventListener('change', () => {
  183. const isFollowMeEnabled = followMeToggle.checked;
  184. emitter.emit(UIEvents.FOLLOW_ME_ENABLED, isFollowMeEnabled);
  185. });
  186. UIUtil.setVisible(wrapperId, true);
  187. }
  188. },
  189. _initializeDeviceSelectionSettings(emitter) {
  190. this.changeDevicesList([]);
  191. $('#selectCamera').change(function () {
  192. let cameraDeviceId = $(this).val();
  193. if (cameraDeviceId !== Settings.getCameraDeviceId()) {
  194. emitter.emit(UIEvents.VIDEO_DEVICE_CHANGED, cameraDeviceId);
  195. }
  196. });
  197. $('#selectMic').change(function () {
  198. let micDeviceId = $(this).val();
  199. if (micDeviceId !== Settings.getMicDeviceId()) {
  200. emitter.emit(UIEvents.AUDIO_DEVICE_CHANGED, micDeviceId);
  201. }
  202. });
  203. $('#selectAudioOutput').change(function () {
  204. let audioOutputDeviceId = $(this).val();
  205. if (audioOutputDeviceId !== Settings.getAudioOutputDeviceId()) {
  206. emitter.emit(
  207. UIEvents.AUDIO_OUTPUT_DEVICE_CHANGED, audioOutputDeviceId);
  208. }
  209. });
  210. },
  211. /**
  212. * If start audio muted/start video muted options should be visible or not.
  213. * @param {boolean} show
  214. */
  215. showStartMutedOptions (show) {
  216. if (show && UIUtil.isSettingEnabled('moderator')) {
  217. // Only show the subtitle if this isn't the only setting section.
  218. if (!$("#moderatorOptionsTitle").is(":visible")
  219. && interfaceConfig.SETTINGS_SECTIONS.length > 1)
  220. UIUtil.setVisible("moderatorOptionsTitle", true);
  221. UIUtil.setVisible("startMutedOptions", true);
  222. } else {
  223. // Only show the subtitle if this isn't the only setting section.
  224. if ($("#moderatorOptionsTitle").is(":visible"))
  225. UIUtil.setVisible("moderatorOptionsTitle", false);
  226. UIUtil.setVisible("startMutedOptions", false);
  227. }
  228. },
  229. updateStartMutedBox (startAudioMuted, startVideoMuted) {
  230. $("#startAudioMuted").attr("checked", startAudioMuted);
  231. $("#startVideoMuted").attr("checked", startVideoMuted);
  232. },
  233. /**
  234. * Shows/hides the follow me options in the settings dialog.
  235. *
  236. * @param {boolean} show {true} to show those options, {false} to hide them
  237. */
  238. showFollowMeOptions (show) {
  239. UIUtil.setVisible(
  240. "followMeOptions",
  241. show && UIUtil.isSettingEnabled('moderator'));
  242. },
  243. /**
  244. * Check if settings menu is visible or not.
  245. * @returns {boolean}
  246. */
  247. isVisible () {
  248. return UIUtil.isVisible(document.getElementById("settings_container"));
  249. },
  250. /**
  251. * Sets microphone's <select> element to select microphone ID from settings.
  252. */
  253. setSelectedMicFromSettings () {
  254. $('#selectMic').val(Settings.getMicDeviceId());
  255. },
  256. /**
  257. * Sets camera's <select> element to select camera ID from settings.
  258. */
  259. setSelectedCameraFromSettings () {
  260. $('#selectCamera').val(Settings.getCameraDeviceId());
  261. },
  262. /**
  263. * Sets audio outputs's <select> element to select audio output ID from
  264. * settings.
  265. */
  266. setSelectedAudioOutputFromSettings () {
  267. $('#selectAudioOutput').val(Settings.getAudioOutputDeviceId());
  268. },
  269. /**
  270. * Change available cameras/microphones or hide selects completely if
  271. * no devices available.
  272. * @param {{ deviceId, label, kind }[]} devices list of available devices
  273. */
  274. changeDevicesList (devices) {
  275. let $selectCamera= AJS.$('#selectCamera'),
  276. $selectMic = AJS.$('#selectMic'),
  277. $selectAudioOutput = AJS.$('#selectAudioOutput'),
  278. $selectAudioOutputParent = $selectAudioOutput.parent();
  279. let audio = devices.filter(device => device.kind === 'audioinput'),
  280. video = devices.filter(device => device.kind === 'videoinput'),
  281. audioOutput = devices
  282. .filter(device => device.kind === 'audiooutput'),
  283. selectedAudioDevice = audio.find(
  284. d => d.deviceId === Settings.getMicDeviceId()) || audio[0],
  285. selectedVideoDevice = video.find(
  286. d => d.deviceId === Settings.getCameraDeviceId()) || video[0],
  287. selectedAudioOutputDevice = audioOutput.find(
  288. d => d.deviceId === Settings.getAudioOutputDeviceId()),
  289. videoPermissionGranted =
  290. JitsiMeetJS.mediaDevices.isDevicePermissionGranted('video'),
  291. audioPermissionGranted =
  292. JitsiMeetJS.mediaDevices.isDevicePermissionGranted('audio');
  293. $selectCamera
  294. .html(generateDevicesOptions(
  295. video,
  296. selectedVideoDevice ? selectedVideoDevice.deviceId : '',
  297. videoPermissionGranted))
  298. .prop('disabled', !video.length || !videoPermissionGranted);
  299. initSelect2($selectCamera);
  300. $selectMic
  301. .html(generateDevicesOptions(
  302. audio,
  303. selectedAudioDevice ? selectedAudioDevice.deviceId : '',
  304. audioPermissionGranted))
  305. .prop('disabled', !audio.length || !audioPermissionGranted);
  306. initSelect2($selectMic);
  307. if (JitsiMeetJS.mediaDevices.isDeviceChangeAvailable('output')) {
  308. $selectAudioOutput
  309. .html(generateDevicesOptions(
  310. audioOutput,
  311. selectedAudioOutputDevice
  312. ? selectedAudioOutputDevice.deviceId
  313. : 'default',
  314. videoPermissionGranted || audioPermissionGranted))
  315. .prop('disabled', !audioOutput.length ||
  316. (!videoPermissionGranted && !audioPermissionGranted));
  317. initSelect2($selectAudioOutput);
  318. $selectAudioOutputParent.show();
  319. } else {
  320. $selectAudioOutputParent.hide();
  321. }
  322. APP.translation.translateElement($('#settings_container option'));
  323. }
  324. };