diff --git a/connection_optimization/do_external_connect.js b/connection_optimization/do_external_connect.js
index c1d32c988..2ce15f02d 100644
--- a/connection_optimization/do_external_connect.js
+++ b/connection_optimization/do_external_connect.js
@@ -1,9 +1,7 @@
/* global config, createConnectionExternally */
-import {
- getRoomName,
- parseURLParams
-} from '../react/features/base/config/functions';
+import getRoomName from '../react/features/base/config/getRoomName';
+import parseURLParams from '../react/features/base/config/parseURLParams';
/**
* Implements external connect using createConnectionExternally function defined
diff --git a/modules/API/constants.js b/modules/API/constants.js
index 6f2c592da..600dfc323 100644
--- a/modules/API/constants.js
+++ b/modules/API/constants.js
@@ -1,9 +1,13 @@
-declare var getConfigParamsFromUrl: Function;
+// XXX The function parseURLParams is exported by the feature base/config (as
+// defined in the terminology of react/). However, this file is (very likely)
+// bundled in external_api in addition to app.bundle and, consequently, it is
+// best to import as little as possible here (rather than the whole feature
+// base/config) in order to minimize the amount of source code bundled into
+// multiple bundles.
+import parseURLParams from '../../react/features/base/config/parseURLParams';
/**
* JitsiMeetExternalAPI id - unique for a webpage.
*/
export const API_ID
- = typeof getConfigParamsFromUrl === 'function'
- ? getConfigParamsFromUrl().jitsi_meet_external_api_id
- : undefined;
+ = parseURLParams(window.location).jitsi_meet_external_api_id;
diff --git a/modules/config/BoshAddressChoice.js b/modules/config/BoshAddressChoice.js
deleted file mode 100644
index 1a4ec4b95..000000000
--- a/modules/config/BoshAddressChoice.js
+++ /dev/null
@@ -1,46 +0,0 @@
-const logger = require("jitsi-meet-logger").getLogger(__filename);
-
-var JSSHA = require('jssha');
-
-module.exports = {
- /**
- * Looks for a list of possible BOSH addresses in 'config.boshList' and
- * sets the value of 'config.bosh' based on that list and 'roomName'.
- * @param config the configuration object.
- * @param roomName the name of the room/conference.
- */
- chooseAddress: function(config, roomName) {
- if (!roomName || !config.boshList || !Array.isArray(config.boshList) ||
- !config.boshList.length) {
- return;
- }
-
- // This implements the actual choice of an entry in the list based on
- // roomName. Please consider the implications for existing deployments
- // before introducing changes.
- var hash = (new JSSHA(roomName, 'TEXT')).getHash('SHA-1', 'HEX');
- var n = parseInt("0x"+hash.substr(-6));
- var idx = n % config.boshList.length;
- var attemptFirstAddress;
-
- config.bosh = config.boshList[idx];
- logger.log('Setting config.bosh to ' + config.bosh +
- ' (idx=' + idx + ')');
-
- if (config.boshAttemptFirstList &&
- Array.isArray(config.boshAttemptFirstList) &&
- config.boshAttemptFirstList.length > 0) {
-
- idx = n % config.boshAttemptFirstList.length;
- attemptFirstAddress = config.boshAttemptFirstList[idx];
-
- if (attemptFirstAddress != config.bosh) {
- config.boshAttemptFirst = attemptFirstAddress;
- logger.log('Setting config.boshAttemptFirst=' +
- attemptFirstAddress + ' (idx=' + idx + ')');
- } else {
- logger.log('Not setting boshAttemptFirst, address matches.');
- }
- }
- }
-};
diff --git a/modules/config/HttpConfigFetch.js b/modules/config/HttpConfigFetch.js
deleted file mode 100644
index 0c7a59884..000000000
--- a/modules/config/HttpConfigFetch.js
+++ /dev/null
@@ -1,52 +0,0 @@
-/* global $, config, interfaceConfig */
-const logger = require("jitsi-meet-logger").getLogger(__filename);
-
-var configUtil = require('./Util');
-
-var HttpConfig = {
- /**
- * Sends HTTP POST request to specified endpoint. In request
- * the name of the room is included in JSON format:
- * {
- * "rooomName": "someroom12345"
- * }
- * @param endpoint the name of HTTP endpoint to which HTTP POST request will
- * be sent.
- * @param roomName the name of the conference room for which config will be
- * requested.
- * @param complete
- */
- obtainConfig: function (endpoint, roomName, complete) {
- logger.info(
- "Send config request to " + endpoint + " for room: " + roomName);
-
-
- $.ajax(
- endpoint,
- {
- method: 'POST',
- contentType: 'application/json',
- data: JSON.stringify({"roomName": roomName}),
- dataType: 'json',
- error: function(jqXHR, textStatus, errorThrown) {
- logger.error("Get config error: ", jqXHR, errorThrown);
- var error = "Get config response status: " + textStatus;
- complete(false, error);
- },
- success: function(data) {
- try {
- configUtil.overrideConfigJSON(
- config, interfaceConfig, data);
- complete(true);
- return;
- } catch (exception) {
- logger.error("Parse config error: ", exception);
- complete(false, exception);
- }
- }
- }
- );
- }
-};
-
-module.exports = HttpConfig;
diff --git a/modules/config/URLProcessor.js b/modules/config/URLProcessor.js
deleted file mode 100644
index 23c0c40e8..000000000
--- a/modules/config/URLProcessor.js
+++ /dev/null
@@ -1,93 +0,0 @@
-/* global config, interfaceConfig, loggingConfig */
-
-import { parseURLParams } from '../../react/features/base/config';
-
-import configUtils from './Util';
-
-const logger = require("jitsi-meet-logger").getLogger(__filename);
-
-/**
- * URL params with this prefix should be merged to config.
- */
-const CONFIG_PREFIX = 'config.';
-
-/**
- * URL params with this prefix should be merged to interface config.
- */
-const INTERFACE_CONFIG_PREFIX = 'interfaceConfig.';
-
-/**
- * Config keys to be ignored.
- *
- * @type Set
- */
-const KEYS_TO_IGNORE = new Set([
- 'analyticsScriptUrls',
- 'callStatsCustomScriptUrl'
-]);
-
-/**
- * URL params with this prefix should be merged to logging config.
- */
-const LOGGING_CONFIG_PREFIX = 'loggingConfig.';
-
-/**
- * Convert 'URL_PARAMS' to JSON object
- * We have:
- * {
- * "config.disableAudioLevels": false,
- * "config.channelLastN": -1,
- * "interfaceConfig.APP_NAME": "Jitsi Meet"
- * }
- * We want to have:
- * {
- * "config": {
- * "disableAudioLevels": false,
- * "channelLastN": -1
- * },
- * interfaceConfig: {
- * "APP_NAME": "Jitsi Meet"
- * }
- * }
- */
-export function setConfigParametersFromUrl() {
- // Parsing config params from URL hash.
- const params = parseURLParams(window.location);
-
- const configJSON = {
- config: {},
- interfaceConfig: {},
- loggingConfig: {}
- };
-
- for (const key in params) {
- if (typeof key === 'string') {
- let confObj = null;
- let confKey;
-
- if (key.indexOf(CONFIG_PREFIX) === 0) {
- confObj = configJSON.config;
- confKey = key.substr(CONFIG_PREFIX.length);
-
- } else if (key.indexOf(INTERFACE_CONFIG_PREFIX) === 0) {
- confObj = configJSON.interfaceConfig;
- confKey
- = key.substr(INTERFACE_CONFIG_PREFIX.length);
- } else if (key.indexOf(LOGGING_CONFIG_PREFIX) === 0) {
- confObj = configJSON.loggingConfig;
- confKey = key.substr(LOGGING_CONFIG_PREFIX.length);
- }
-
- // prevent passing some parameters which can inject scripts
- if (confObj && !KEYS_TO_IGNORE.has(confKey)) {
- confObj[confKey] = params[key];
- }
- } else {
- logger.warn('Invalid config key: ', key);
- }
- }
-
- configUtils.overrideConfigJSON(
- config, interfaceConfig, loggingConfig,
- configJSON);
-}
diff --git a/modules/config/Util.js b/modules/config/Util.js
deleted file mode 100644
index 7d7065f56..000000000
--- a/modules/config/Util.js
+++ /dev/null
@@ -1,54 +0,0 @@
-const logger = require("jitsi-meet-logger").getLogger(__filename);
-
-var ConfigUtil = {
- /**
- * Method overrides JSON properties in config and
- * interfaceConfig Objects with the values from newConfig
- * @param config the config object for which we'll be overriding properties
- * @param interfaceConfig the interfaceConfig object for which we'll be
- * overriding properties.
- * @param loggingConfig the logging config object for which we'll be
- * overriding properties.
- * @param newConfig object containing configuration properties. Destination
- * object is selected based on root property name:
- * {
- * config: {
- * // config.js properties to be
- * },
- * interfaceConfig: {
- * // interface_config.js properties here
- * },
- * loggingConfig: {
- * // logging_config.js properties here
- * }
- * }
- */
- overrideConfigJSON: function (config,
- interfaceConfig, loggingConfig, newConfig) {
- var configRoot, key, value, confObj;
- for (configRoot in newConfig) {
- confObj = null;
- if (configRoot == "config") {
- confObj = config;
- } else if (configRoot == "interfaceConfig") {
- confObj = interfaceConfig;
- } else if (configRoot == "loggingConfig") {
- confObj = loggingConfig;
- } else {
- continue;
- }
-
- for (key in newConfig[configRoot]) {
- value = newConfig[configRoot][key];
- if (confObj[key] && typeof confObj[key] !== typeof value) {
- logger.log("Overriding a " + configRoot +
- " property with a property of different type.");
- }
- logger.info("Overriding " + key + " with: " + value);
- confObj[key] = value;
- }
- }
- }
-};
-
-module.exports = ConfigUtil;
diff --git a/react/features/app/actions.js b/react/features/app/actions.js
index fac887a23..376ebbc08 100644
--- a/react/features/app/actions.js
+++ b/react/features/app/actions.js
@@ -18,7 +18,8 @@ import {
* @returns {Function}
*/
export function appInit() {
- return (dispatch, getState) => init(getState());
+ return (dispatch: Dispatch<*>, getState: Function) =>
+ init(getState());
}
/**
@@ -31,7 +32,7 @@ export function appInit() {
* @returns {Function}
*/
export function appNavigate(uri) {
- return (dispatch, getState) => {
+ return (dispatch: Dispatch<*>, getState: Function) => {
const state = getState();
const oldDomain = getDomain(state);
const defaultURL = state['features/app'].app._getDefaultURL();
@@ -43,14 +44,12 @@ export function appNavigate(uri) {
// If the specified URI does not identify a domain, use the app's
// default.
if (typeof domain === 'undefined') {
- domain
- = _parseURIString(defaultURL)
- .domain;
+ domain = _parseURIString(defaultURL).domain;
}
if (room) {
- const splitUrl = uri.split(domain);
- const urlWithoutDomain = splitUrl[splitUrl.length - 1];
+ const splitURL = uri.split(domain);
+ const urlWithoutDomain = splitURL[splitURL.length - 1];
urlObject = new URL(urlWithoutDomain, `https://${domain}`);
}
diff --git a/react/features/base/config/functions.js b/react/features/base/config/functions.js
index 39d84b85e..e2c615ec1 100644
--- a/react/features/base/config/functions.js
+++ b/react/features/base/config/functions.js
@@ -1,72 +1,247 @@
/* @flow */
-declare var config: Object;
+import JSSHA from 'jssha';
+
+import parseURLParams from './parseURLParams';
+
+declare var $: Object;
/**
- * Builds and returns the room name.
+ * The config keys to ignore because, for example, their values identify scripts
+ * and it is not desireable to inject these through URL params.
*
- * @returns {string}
+ * @private
+ * @type Array
*/
-export function getRoomName(): ?string {
- const { getroomnode } = config;
- const path = window.location.pathname;
- let roomName;
+const _KEYS_TO_IGNORE = [
+ 'analyticsScriptUrls',
+ 'callStatsCustomScriptUrl'
+];
- // Determine the room node from the URL.
- if (getroomnode && typeof getroomnode === 'function') {
- roomName = getroomnode.call(config, path);
- } else {
- // Fall back to the default strategy of making assumptions about how the
- // URL maps to the room (name). It currently assumes a deployment in
- // which the last non-directory component of the path (name) is the
- // room.
- roomName
- = path.substring(path.lastIndexOf('/') + 1).toLowerCase()
- || undefined;
+const logger = require('jitsi-meet-logger').getLogger(__filename);
+
+// XXX The functions getRoomName and parseURLParams are split out of
+// functions.js because they are bundled in both app.bundle and
+// do_external_connect, webpack 1 does not support tree shaking, and we don't
+// want all functions to be bundled in do_external_connect.
+export { default as getRoomName } from './getRoomName';
+export { parseURLParams };
+
+/* eslint-disable no-shadow */
+
+/**
+ * Looks for a list of possible BOSH addresses in {@code config.boshList} and
+ * sets the value of {@code config.bosh} based on that list and
+ * {@code roomName}.
+ *
+ * @param {Object} config - The configuration object.
+ * @param {string} roomName - The name of the room/conference.
+ * @returns {void}
+ */
+export function chooseBOSHAddress(config: Object, roomName: string) {
+ if (!roomName) {
+ return;
}
- return roomName;
+ const { boshList } = config;
+
+ if (!boshList || !Array.isArray(boshList) || !boshList.length) {
+ return;
+ }
+
+ // This implements the actual choice of an entry in the list based on
+ // roomName. Please consider the implications for existing deployments
+ // before introducing changes.
+ const hash = (new JSSHA(roomName, 'TEXT')).getHash('SHA-1', 'HEX');
+ const n = parseInt(hash.substr(-6), 16);
+ let idx = n % boshList.length;
+
+ config.bosh = boshList[idx];
+ logger.log(`Setting config.bosh to ${config.bosh} (idx=${idx})`);
+
+ const { boshAttemptFirstList } = config;
+
+ if (boshAttemptFirstList
+ && Array.isArray(boshAttemptFirstList)
+ && boshAttemptFirstList.length > 0) {
+ idx = n % boshAttemptFirstList.length;
+
+ const attemptFirstAddress = boshAttemptFirstList[idx];
+
+ if (attemptFirstAddress === config.bosh) {
+ logger.log('Not setting config.boshAttemptFirst, address matches.');
+ } else {
+ config.boshAttemptFirst = attemptFirstAddress;
+ logger.log(
+ `Setting config.boshAttemptFirst=${attemptFirstAddress} (idx=${
+ idx})`);
+ }
+ }
}
+/* eslint-enable no-shadow */
+
/**
- * Parses the parameters from the URL and returns them as a JS object.
+ * Sends HTTP POST request to specified endpoint. In request the name
+ * of the room is included in JSON format:
+ * {
+ * "rooomName": "someroom12345"
+ * }.
*
- * @param {string} url - URL to parse.
- * @param {boolean} dontParse - If false or undefined some transformations
- * (for parsing the value as JSON) are going to be executed.
- * @param {string} source - Values - "hash"/"search" if "search" the parameters
- * will parsed from location.search otherwise from location.hash.
- * @returns {Object}
+ * @param {string} endpoint - The name of HTTP endpoint to which to send
+ * the HTTP POST request.
+ * @param {string} roomName - The name of the conference room for which config
+ * is requested.
+ * @param {Function} complete - The callback to invoke upon success or failure.
+ * @returns {void}
*/
-export function parseURLParams(
- url: URL,
- dontParse: boolean = false,
- source: string = 'hash'): Object {
- const paramStr = source === 'search' ? url.search : url.hash;
- const params = {};
+export function obtainConfig(
+ endpoint: string,
+ roomName: string,
+ complete: Function) {
+ logger.info(`Send config request to ${endpoint} for room: ${roomName}`);
+ $.ajax(
+ endpoint,
+ {
+ contentType: 'application/json',
+ data: JSON.stringify({ roomName }),
+ dataType: 'json',
+ method: 'POST',
- // eslint-disable-next-line newline-per-chained-call
- paramStr && paramStr.substr(1).split('&').forEach(part => {
- const param = part.split('=');
- let value;
+ error(jqXHR, textStatus, errorThrown) {
+ logger.error('Get config error: ', jqXHR, errorThrown);
+ complete(false, `Get config response status: ${textStatus}`);
+ },
+ success(data) {
+ const { config, interfaceConfig, loggingConfig } = window;
- try {
- value = param[1];
- if (!dontParse) {
- value
- = JSON.parse(
- decodeURIComponent(param[1]).replace(/\\&/, '&'));
+ try {
+ overrideConfigJSON(
+ config, interfaceConfig, loggingConfig,
+ data);
+ complete(true);
+ } catch (e) {
+ logger.error('Parse config error: ', e);
+ complete(false, e);
+ }
}
- } catch (e) {
- const msg = `Failed to parse URL parameter value: ${String(value)}`;
-
- console.warn(msg, e);
- window.onerror && window.onerror(msg, null, null, null, e);
-
- return;
}
- params[param[0]] = value;
- });
-
- return params;
+ );
+}
+
+/* eslint-disable max-params, no-shadow */
+
+/**
+ * Overrides JSON properties in {@code config} and
+ * {@code interfaceConfig} Objects with the values from {@code newConfig}.
+ *
+ * @param {Object} config - The config Object in which we'll be overriding
+ * properties.
+ * @param {Object} interfaceConfig - The interfaceConfig Object in which we'll
+ * be overriding properties.
+ * @param {Object} loggingConfig - The loggingConfig Object in which we'll be
+ * overriding properties.
+ * @param {Object} json - Object containing configuration properties.
+ * Destination object is selected based on root property name:
+ * {
+ * config: {
+ * // config.js properties here
+ * },
+ * interfaceConfig: {
+ * // interface_config.js properties here
+ * },
+ * loggingConfig: {
+ * // logging_config.js properties here
+ * }
+ * }.
+ * @returns {void}
+ */
+export function overrideConfigJSON(
+ config: Object, interfaceConfig: Object, loggingConfig: Object,
+ json: Object) {
+ for (const configName of Object.keys(json)) {
+ let configObj;
+
+ if (configName === 'config') {
+ configObj = config;
+ } else if (configName === 'interfaceConfig') {
+ configObj = interfaceConfig;
+ } else if (configName === 'loggingConfig') {
+ configObj = loggingConfig;
+ }
+ if (configObj) {
+ const configJSON = json[configName];
+
+ for (const key of Object.keys(configJSON)) {
+ const oldValue = configObj[key];
+ const newValue = configJSON[key];
+
+ if (oldValue && typeof oldValue !== typeof newValue) {
+ logger.log(
+ `Overriding a ${configName
+ } property with a property of different type.`);
+ }
+ logger.info(`Overriding ${key} with: ${newValue}`);
+ configObj[key] = newValue;
+ }
+ }
+ }
+}
+
+/* eslint-enable max-params, no-shadow */
+
+/**
+ * Converts 'URL_PARAMS' to JSON object.
+ * We have:
+ * {
+ * "config.disableAudioLevels": false,
+ * "config.channelLastN": -1,
+ * "interfaceConfig.APP_NAME": "Jitsi Meet"
+ * }.
+ * We want to have:
+ * {
+ * "config": {
+ * "disableAudioLevels": false,
+ * "channelLastN": -1
+ * },
+ * interfaceConfig: {
+ * "APP_NAME": "Jitsi Meet"
+ * }
+ * }.
+ *
+ * @returns {void}
+ */
+export function setConfigFromURLParams() {
+ const params = parseURLParams(window.location);
+
+ const { config, interfaceConfig, loggingConfig } = window;
+ const json = {};
+
+ // TODO We're still in the middle ground between old Web with config,
+ // interfaceConfig, and loggingConfig used via global variables and new Web
+ // and mobile reading the respective values from the redux store. On React
+ // Native there's no interfaceConfig at all yet and loggingConfig is not
+ // loaded but there's a default value in the redux store.
+ config && (json.config = {});
+ interfaceConfig && (json.interfaceConfig = {});
+ loggingConfig && (json.loggingConfig = {});
+
+ for (const param of Object.keys(params)) {
+ const objEnd = param.indexOf('.');
+
+ if (objEnd !== -1) {
+ const obj = param.substring(0, objEnd);
+
+ if (json.hasOwnProperty(obj)) {
+ const key = param.substring(objEnd + 1);
+
+ // Prevent passing some parameters which can inject scripts.
+ if (key && _KEYS_TO_IGNORE.indexOf(key) === -1) {
+ json[obj][key] = params[param];
+ }
+ }
+ }
+ }
+
+ overrideConfigJSON(config, interfaceConfig, loggingConfig, json);
}
diff --git a/react/features/base/config/getRoomName.js b/react/features/base/config/getRoomName.js
new file mode 100644
index 000000000..d89a928bd
--- /dev/null
+++ b/react/features/base/config/getRoomName.js
@@ -0,0 +1,29 @@
+/* @flow */
+
+declare var config: Object;
+
+/**
+ * Builds and returns the room name.
+ *
+ * @returns {string}
+ */
+export default function getRoomName(): ?string {
+ const { getroomnode } = config;
+ const path = window.location.pathname;
+ let roomName;
+
+ // Determine the room node from the URL.
+ if (getroomnode && typeof getroomnode === 'function') {
+ roomName = getroomnode.call(config, path);
+ } else {
+ // Fall back to the default strategy of making assumptions about how the
+ // URL maps to the room (name). It currently assumes a deployment in
+ // which the last non-directory component of the path (name) is the
+ // room.
+ roomName
+ = path.substring(path.lastIndexOf('/') + 1).toLowerCase()
+ || undefined;
+ }
+
+ return roomName;
+}
diff --git a/react/features/base/config/parseURLParams.js b/react/features/base/config/parseURLParams.js
new file mode 100644
index 000000000..11951a911
--- /dev/null
+++ b/react/features/base/config/parseURLParams.js
@@ -0,0 +1,49 @@
+/* @flow */
+
+/**
+ * Parses the parameters from the URL and returns them as a JS object.
+ *
+ * @param {string} url - URL to parse.
+ * @param {boolean} dontParse - If false or undefined some transformations
+ * (for parsing the value as JSON) are going to be executed.
+ * @param {string} source - Values - "hash"/"search" if "search" the parameters
+ * will parsed from location.search otherwise from location.hash.
+ * @returns {Object}
+ */
+export default function parseURLParams(
+ url: URL,
+ dontParse: boolean = false,
+ source: string = 'hash'): Object {
+ const paramStr = source === 'search' ? url.search : url.hash;
+ const params = {};
+
+ // eslint-disable-next-line newline-per-chained-call
+ paramStr && paramStr.substr(1).split('&').forEach(part => {
+ const param = part.split('=');
+ const key = param[0];
+
+ if (!key) {
+ return;
+ }
+
+ let value;
+
+ try {
+ value = param[1];
+ if (!dontParse) {
+ value
+ = JSON.parse(decodeURIComponent(value).replace(/\\&/, '&'));
+ }
+ } catch (e) {
+ const msg = `Failed to parse URL parameter value: ${String(value)}`;
+
+ console.warn(msg, e);
+ window.onerror && window.onerror(msg, null, null, null, e);
+
+ return;
+ }
+ params[key] = value;
+ });
+
+ return params;
+}
diff --git a/react/features/base/connection/actions.native.js b/react/features/base/connection/actions.native.js
index e3d298321..4c2573ba6 100644
--- a/react/features/base/connection/actions.native.js
+++ b/react/features/base/connection/actions.native.js
@@ -21,11 +21,12 @@ export function connect() {
return (dispatch: Dispatch<*>, getState: Function) => {
const state = getState();
const { options } = state['features/base/connection'];
+ const { issuer, jwt } = state['features/jwt'];
const { room } = state['features/base/conference'];
const connection
= new JitsiMeetJS.JitsiConnection(
options.appId,
- options.token,
+ jwt && issuer && issuer !== 'anonymous' ? jwt : undefined,
{
...options,
bosh:
diff --git a/react/features/base/lib-jitsi-meet/functions.js b/react/features/base/lib-jitsi-meet/functions.js
index a5eb353d4..80a3f0aad 100644
--- a/react/features/base/lib-jitsi-meet/functions.js
+++ b/react/features/base/lib-jitsi-meet/functions.js
@@ -1,8 +1,7 @@
-import { loadScript } from '../../base/util';
+/* @flow */
-import {
- setConfigParametersFromUrl
-} from '../../../../modules/config/URLProcessor';
+import { setConfigFromURLParams } from '../../base/config';
+import { loadScript } from '../../base/util';
import JitsiMeetJS from './_';
@@ -10,6 +9,28 @@ declare var APP: Object;
const JitsiConnectionErrors = JitsiMeetJS.errors.connection;
+/**
+ * Creates a JitsiLocalTrack model from the given device id.
+ *
+ * @param {string} type - The media type of track being created. Expected values
+ * are "video" or "audio".
+ * @param {string} deviceId - The id of the target media source.
+ * @returns {Promise}
+ */
+export function createLocalTrack(type: string, deviceId: string) {
+ return (
+ JitsiMeetJS.createLocalTracks({
+ cameraDeviceId: deviceId,
+ devices: [ type ],
+
+ // eslint-disable-next-line camelcase
+ firefox_fake_device:
+ window.config && window.config.firefox_fake_device,
+ micDeviceId: deviceId
+ })
+ .then(([ jitsiLocalTrack ]) => jitsiLocalTrack));
+}
+
/**
* Determines whether a specific JitsiConnectionErrors instance indicates a
* fatal JitsiConnection error.
@@ -38,56 +59,43 @@ export function isFatalJitsiConnectionError(error: string) {
* @returns {Promise