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.

functions.any.js 8.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. // @flow
  2. import { CONFIG_WHITELIST } from '../config';
  3. import { toState } from '../redux';
  4. import { parseURLParams } from '../util';
  5. import { DEFAULT_SERVER_URL } from './constants';
  6. /**
  7. * Returns the effective value of a configuration/preference/setting by applying
  8. * a precedence among the values specified by JWT, URL, settings,
  9. * and config.
  10. *
  11. * @param {Object|Function} stateful - The redux state object or {@code getState} function.
  12. * @param {string} propertyName - The name of the
  13. * configuration/preference/setting (property) to retrieve.
  14. * @param {Object} sources - Flags indicating the configuration/preference/setting sources to
  15. * consider/retrieve values from.
  16. * @param {boolean} sources.config - Config.
  17. * @param {boolean} jwt - JWT.
  18. * @param {boolean} settings - Settings.
  19. * @param {boolean} urlParams - URL parameters.
  20. * @returns {any}
  21. */
  22. export function getPropertyValue(
  23. stateful: Object | Function,
  24. propertyName: string,
  25. sources?: Object
  26. ) {
  27. // Default values don't play nicely with partial objects and we want to make
  28. // the function easy to use without exhaustively defining all flags:
  29. sources = { // eslint-disable-line no-param-reassign
  30. // Defaults:
  31. config: true,
  32. jwt: true,
  33. settings: true,
  34. urlParams: true,
  35. ...sources
  36. };
  37. // Precedence: jwt -> urlParams -> settings -> config.
  38. const state = toState(stateful);
  39. // jwt
  40. if (sources.jwt) {
  41. const value = state['features/base/jwt'][propertyName];
  42. if (typeof value !== 'undefined') {
  43. return value[propertyName];
  44. }
  45. }
  46. // urlParams
  47. if (sources.urlParams) {
  48. if (CONFIG_WHITELIST.indexOf(propertyName) !== -1) {
  49. const urlParams
  50. = parseURLParams(state['features/base/connection'].locationURL);
  51. const value = urlParams[`config.${propertyName}`];
  52. if (typeof value !== 'undefined') {
  53. return value;
  54. }
  55. }
  56. }
  57. // settings
  58. if (sources.settings) {
  59. const value = state['features/base/settings'][propertyName];
  60. if (typeof value !== 'undefined') {
  61. return value;
  62. }
  63. }
  64. // config
  65. if (sources.config) {
  66. const value = state['features/base/config'][propertyName];
  67. if (typeof value !== 'undefined') {
  68. return value;
  69. }
  70. }
  71. return undefined;
  72. }
  73. /**
  74. * Gets the currently configured server URL.
  75. *
  76. * @param {Object|Function} stateful - The redux state object or
  77. * {@code getState} function.
  78. * @returns {string} - The currently configured server URL.
  79. */
  80. export function getServerURL(stateful: Object | Function) {
  81. const state = toState(stateful);
  82. return state['features/base/settings'].serverURL || DEFAULT_SERVER_URL;
  83. }
  84. /**
  85. * Searches known devices for a matching deviceId and fall back to matching on
  86. * label. Returns the stored preferred cameraDeviceId if a match is not found.
  87. *
  88. * @param {Object|Function} stateful - The redux state object or
  89. * {@code getState} function.
  90. * @returns {string}
  91. */
  92. export function getUserSelectedCameraDeviceId(stateful: Object | Function) {
  93. const state = toState(stateful);
  94. const {
  95. userSelectedCameraDeviceId,
  96. userSelectedCameraDeviceLabel
  97. } = state['features/base/settings'];
  98. const { videoInput } = state['features/base/devices'].availableDevices;
  99. return _getUserSelectedDeviceId({
  100. availableDevices: videoInput,
  101. // Operating systems may append " #{number}" somewhere in the label so
  102. // find and strip that bit.
  103. matchRegex: /\s#\d*(?!.*\s#\d*)/,
  104. userSelectedDeviceId: userSelectedCameraDeviceId,
  105. userSelectedDeviceLabel: userSelectedCameraDeviceLabel,
  106. replacement: ''
  107. });
  108. }
  109. /**
  110. * Searches known devices for a matching deviceId and fall back to matching on
  111. * label. Returns the stored preferred micDeviceId if a match is not found.
  112. *
  113. * @param {Object|Function} stateful - The redux state object or
  114. * {@code getState} function.
  115. * @returns {string}
  116. */
  117. export function getUserSelectedMicDeviceId(stateful: Object | Function) {
  118. const state = toState(stateful);
  119. const {
  120. userSelectedMicDeviceId,
  121. userSelectedMicDeviceLabel
  122. } = state['features/base/settings'];
  123. const { audioInput } = state['features/base/devices'].availableDevices;
  124. return _getUserSelectedDeviceId({
  125. availableDevices: audioInput,
  126. // Operating systems may append " ({number}-" somewhere in the label so
  127. // find and strip that bit.
  128. matchRegex: /\s\(\d*-\s(?!.*\s\(\d*-\s)/,
  129. userSelectedDeviceId: userSelectedMicDeviceId,
  130. userSelectedDeviceLabel: userSelectedMicDeviceLabel,
  131. replacement: ' ('
  132. });
  133. }
  134. /**
  135. * Searches known devices for a matching deviceId and fall back to matching on
  136. * label. Returns the stored preferred audioOutputDeviceId if a match is not found.
  137. *
  138. * @param {Object|Function} stateful - The redux state object or
  139. * {@code getState} function.
  140. * @returns {string}
  141. */
  142. export function getUserSelectedOutputDeviceId(stateful: Object | Function) {
  143. const state = toState(stateful);
  144. const {
  145. userSelectedAudioOutputDeviceId,
  146. userSelectedAudioOutputDeviceLabel
  147. } = state['features/base/settings'];
  148. const { audioOutput } = state['features/base/devices'].availableDevices;
  149. return _getUserSelectedDeviceId({
  150. availableDevices: audioOutput,
  151. matchRegex: undefined,
  152. userSelectedDeviceId: userSelectedAudioOutputDeviceId,
  153. userSelectedDeviceLabel: userSelectedAudioOutputDeviceLabel,
  154. replacement: undefined
  155. });
  156. }
  157. /**
  158. * A helper function to abstract the logic for choosing which device ID to
  159. * use. Falls back to fuzzy matching on label if a device ID match is not found.
  160. *
  161. * @param {Object} options - The arguments used to match find the preferred
  162. * device ID from available devices.
  163. * @param {Array<string>} options.availableDevices - The array of currently
  164. * available devices to match against.
  165. * @param {Object} options.matchRegex - The regex to use to find strings
  166. * appended to the label by the operating system. The matches will be replaced
  167. * with options.replacement, with the intent of matching the same device that
  168. * might have a modified label.
  169. * @param {string} options.userSelectedDeviceId - The device ID the participant
  170. * prefers to use.
  171. * @param {string} options.userSelectedDeviceLabel - The label associated with the
  172. * device ID the participant prefers to use.
  173. * @param {string} options.replacement - The string to use with
  174. * options.matchRegex to remove identifies added to the label by the operating
  175. * system.
  176. * @private
  177. * @returns {string} The preferred device ID to use for media.
  178. */
  179. function _getUserSelectedDeviceId(options) {
  180. const {
  181. availableDevices,
  182. matchRegex,
  183. userSelectedDeviceId,
  184. userSelectedDeviceLabel,
  185. replacement
  186. } = options;
  187. // If there is no label at all, there is no need to fall back to checking
  188. // the label for a fuzzy match.
  189. if (!userSelectedDeviceLabel || !userSelectedDeviceId) {
  190. return userSelectedDeviceId;
  191. }
  192. const foundMatchingBasedonDeviceId = availableDevices.find(
  193. candidate => candidate.deviceId === userSelectedDeviceId);
  194. // Prioritize matching the deviceId
  195. if (foundMatchingBasedonDeviceId) {
  196. return userSelectedDeviceId;
  197. }
  198. const strippedDeviceLabel
  199. = matchRegex ? userSelectedDeviceLabel.replace(matchRegex, replacement)
  200. : userSelectedDeviceLabel;
  201. const foundMatchBasedOnLabel = availableDevices.find(candidate => {
  202. const { label } = candidate;
  203. if (!label) {
  204. return false;
  205. } else if (strippedDeviceLabel === label) {
  206. return true;
  207. }
  208. const strippedCandidateLabel
  209. = label.replace(matchRegex, replacement);
  210. return strippedDeviceLabel === strippedCandidateLabel;
  211. });
  212. return foundMatchBasedOnLabel
  213. ? foundMatchBasedOnLabel.deviceId : userSelectedDeviceId;
  214. }
  215. /**
  216. * Should we hide the helper dialog when a user tries to do audio only screen sharing.
  217. *
  218. * @param {Object} state - The state of the application.
  219. * @returns {boolean}
  220. */
  221. export function shouldHideShareAudioHelper(state: Object): boolean {
  222. return state['features/base/settings'].hideShareAudioHelper;
  223. }