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.js 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. // @flow
  2. import { getAvatarURL as _getAvatarURL } from 'js-utils/avatar';
  3. import { toState } from '../redux';
  4. import { JitsiParticipantConnectionStatus } from '../lib-jitsi-meet';
  5. import { MEDIA_TYPE, shouldRenderVideoTrack } from '../media';
  6. import { getTrackByMediaTypeAndParticipant } from '../tracks';
  7. import {
  8. DEFAULT_AVATAR_RELATIVE_PATH,
  9. LOCAL_PARTICIPANT_DEFAULT_ID,
  10. MAX_DISPLAY_NAME_LENGTH,
  11. PARTICIPANT_ROLE
  12. } from './constants';
  13. declare var config: Object;
  14. declare var interfaceConfig: Object;
  15. /**
  16. * Returns the URL of the image for the avatar of a specific participant.
  17. *
  18. * @param {Participant} [participant] - The participant to return the avatar URL
  19. * of.
  20. * @param {string} [participant.avatarID] - Participant's avatar ID.
  21. * @param {string} [participant.avatarURL] - Participant's avatar URL.
  22. * @param {string} [participant.email] - Participant's e-mail address.
  23. * @param {string} [participant.id] - Participant's ID.
  24. * @public
  25. * @returns {string} The URL of the image for the avatar of the specified
  26. * participant.
  27. */
  28. export function getAvatarURL({ avatarID, avatarURL, email, id }: {
  29. avatarID: string,
  30. avatarURL: string,
  31. email: string,
  32. id: string
  33. }) {
  34. // If disableThirdPartyRequests disables third-party avatar services, we are
  35. // restricted to a stock image of ours.
  36. if (typeof config === 'object' && config.disableThirdPartyRequests) {
  37. return DEFAULT_AVATAR_RELATIVE_PATH;
  38. }
  39. // If an avatarURL is specified, then obviously there's nothing to generate.
  40. if (avatarURL) {
  41. return avatarURL;
  42. }
  43. // The deployment is allowed to choose the avatar service which is to
  44. // generate the random avatars.
  45. const avatarService
  46. = typeof interfaceConfig === 'object'
  47. && interfaceConfig.RANDOM_AVATAR_URL_PREFIX
  48. ? {
  49. urlPrefix: interfaceConfig.RANDOM_AVATAR_URL_PREFIX,
  50. urlSuffix: interfaceConfig.RANDOM_AVATAR_URL_SUFFIX }
  51. : undefined;
  52. // eslint-disable-next-line object-property-newline
  53. return _getAvatarURL({ avatarID, email, id }, avatarService);
  54. }
  55. /**
  56. * Returns the avatarURL for the participant associated with the passed in
  57. * participant ID.
  58. *
  59. * @param {(Function|Object|Participant[])} stateful - The redux state
  60. * features/base/participants, the (whole) redux state, or redux's
  61. * {@code getState} function to be used to retrieve the state
  62. * features/base/participants.
  63. * @param {string} id - The ID of the participant to retrieve.
  64. * @param {boolean} isLocal - An optional parameter indicating whether or not
  65. * the partcipant id is for the local user. If true, a different logic flow is
  66. * used find the local user, ignoring the id value as it can change through the
  67. * beginning and end of a call.
  68. * @returns {(string|undefined)}
  69. */
  70. export function getAvatarURLByParticipantId(
  71. stateful: Object | Function,
  72. id: string = LOCAL_PARTICIPANT_DEFAULT_ID) {
  73. const participant = getParticipantById(stateful, id);
  74. return participant && getAvatarURL(participant);
  75. }
  76. /**
  77. * Returns local participant from Redux state.
  78. *
  79. * @param {(Function|Object|Participant[])} stateful - The redux state
  80. * features/base/participants, the (whole) redux state, or redux's
  81. * {@code getState} function to be used to retrieve the state
  82. * features/base/participants.
  83. * @returns {(Participant|undefined)}
  84. */
  85. export function getLocalParticipant(stateful: Object | Function) {
  86. const participants = _getAllParticipants(stateful);
  87. return participants.find(p => p.local);
  88. }
  89. /**
  90. * Normalizes a display name so then no invalid values (padding, length...etc)
  91. * can be set.
  92. *
  93. * @param {string} name - The display name to set.
  94. * @returns {string}
  95. */
  96. export function getNormalizedDisplayName(name: string) {
  97. if (!name || !name.trim()) {
  98. return undefined;
  99. }
  100. return name.trim().substring(0, MAX_DISPLAY_NAME_LENGTH);
  101. }
  102. /**
  103. * Returns participant by ID from Redux state.
  104. *
  105. * @param {(Function|Object|Participant[])} stateful - The redux state
  106. * features/base/participants, the (whole) redux state, or redux's
  107. * {@code getState} function to be used to retrieve the state
  108. * features/base/participants.
  109. * @param {string} id - The ID of the participant to retrieve.
  110. * @private
  111. * @returns {(Participant|undefined)}
  112. */
  113. export function getParticipantById(
  114. stateful: Object | Function, id: string): ?Object {
  115. const participants = _getAllParticipants(stateful);
  116. return participants.find(p => p.id === id);
  117. }
  118. /**
  119. * Returns a count of the known participants in the passed in redux state,
  120. * excluding any fake participants.
  121. *
  122. * @param {(Function|Object|Participant[])} stateful - The redux state
  123. * features/base/participants, the (whole) redux state, or redux's
  124. * {@code getState} function to be used to retrieve the state
  125. * features/base/participants.
  126. * @returns {number}
  127. */
  128. export function getParticipantCount(stateful: Object | Function) {
  129. return getParticipants(stateful).length;
  130. }
  131. /**
  132. * Returns a count of the known participants in the passed in redux state,
  133. * including fake participants.
  134. *
  135. * @param {(Function|Object|Participant[])} stateful - The redux state
  136. * features/base/participants, the (whole) redux state, or redux's
  137. * {@code getState} function to be used to retrieve the state
  138. * features/base/participants.
  139. * @returns {number}
  140. */
  141. export function getParticipantCountWithFake(stateful: Object | Function) {
  142. return _getAllParticipants(stateful).length;
  143. }
  144. /**
  145. * Returns participant's display name.
  146. *
  147. * FIXME: Remove the hardcoded strings once interfaceConfig is stored in redux
  148. * and merge with a similarly named method in {@code conference.js}.
  149. *
  150. * @param {(Function|Object)} stateful - The (whole) redux state, or redux's
  151. * {@code getState} function to be used to retrieve the state.
  152. * @param {string} id - The ID of the participant's display name to retrieve.
  153. * @private
  154. * @returns {string}
  155. */
  156. export function getParticipantDisplayName(
  157. stateful: Object | Function,
  158. id: string) {
  159. const participant = getParticipantById(stateful, id);
  160. if (participant) {
  161. if (participant.name) {
  162. return participant.name;
  163. }
  164. if (participant.local) {
  165. return typeof interfaceConfig === 'object'
  166. ? interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME
  167. : 'me';
  168. }
  169. }
  170. return typeof interfaceConfig === 'object'
  171. ? interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME
  172. : 'Fellow Jitster';
  173. }
  174. /**
  175. * Returns the presence status of a participant associated with the passed id.
  176. *
  177. * @param {(Function|Object)} stateful - The (whole) redux state, or redux's
  178. * {@code getState} function to be used to retrieve the state.
  179. * @param {string} id - The id of the participant.
  180. * @returns {string} - The presence status.
  181. */
  182. export function getParticipantPresenceStatus(
  183. stateful: Object | Function, id: string) {
  184. if (!id) {
  185. return undefined;
  186. }
  187. const participantById = getParticipantById(stateful, id);
  188. if (!participantById) {
  189. return undefined;
  190. }
  191. return participantById.presence;
  192. }
  193. /**
  194. * Selectors for getting all known participants with fake participants filtered
  195. * out.
  196. *
  197. * @param {(Function|Object|Participant[])} stateful - The redux state
  198. * features/base/participants, the (whole) redux state, or redux's
  199. * {@code getState} function to be used to retrieve the state
  200. * features/base/participants.
  201. * @returns {Participant[]}
  202. */
  203. export function getParticipants(stateful: Object | Function) {
  204. return _getAllParticipants(stateful).filter(p => !p.isFakeParticipant);
  205. }
  206. /**
  207. * Returns the participant which has its pinned state set to truthy.
  208. *
  209. * @param {(Function|Object|Participant[])} stateful - The redux state
  210. * features/base/participants, the (whole) redux state, or redux's
  211. * {@code getState} function to be used to retrieve the state
  212. * features/base/participants.
  213. * @returns {(Participant|undefined)}
  214. */
  215. export function getPinnedParticipant(stateful: Object | Function) {
  216. return _getAllParticipants(stateful).find(p => p.pinned);
  217. }
  218. /**
  219. * Returns array of participants from Redux state.
  220. *
  221. * @param {(Function|Object|Participant[])} stateful - The redux state
  222. * features/base/participants, the (whole) redux state, or redux's
  223. * {@code getState} function to be used to retrieve the state
  224. * features/base/participants.
  225. * @private
  226. * @returns {Participant[]}
  227. */
  228. function _getAllParticipants(stateful) {
  229. return (
  230. Array.isArray(stateful)
  231. ? stateful
  232. : toState(stateful)['features/base/participants'] || []);
  233. }
  234. /**
  235. * Returns true if the current local participant is a moderator in the
  236. * conference.
  237. *
  238. * @param {Object|Function} stateful - Object or function that can be resolved
  239. * to the Redux state.
  240. * @returns {boolean}
  241. */
  242. export function isLocalParticipantModerator(stateful: Object | Function) {
  243. const state = toState(stateful);
  244. const localParticipant = getLocalParticipant(state);
  245. if (!localParticipant) {
  246. return false;
  247. }
  248. return (
  249. localParticipant.role === PARTICIPANT_ROLE.MODERATOR
  250. && (!state['features/base/config'].enableUserRolesBasedOnToken
  251. || !state['features/base/jwt'].isGuest));
  252. }
  253. /**
  254. * Returns true if the video of the participant should be rendered.
  255. *
  256. * @param {Object|Function} stateful - Object or function that can be resolved
  257. * to the Redux state.
  258. * @param {string} id - The ID of the participant.
  259. * @returns {boolean}
  260. */
  261. export function shouldRenderParticipantVideo(
  262. stateful: Object | Function, id: string) {
  263. const state = toState(stateful);
  264. const participant = getParticipantById(state, id);
  265. if (!participant) {
  266. return false;
  267. }
  268. const audioOnly = state['features/base/conference'].audioOnly;
  269. const connectionStatus = participant.connectionStatus
  270. || JitsiParticipantConnectionStatus.ACTIVE;
  271. const videoTrack = getTrackByMediaTypeAndParticipant(
  272. state['features/base/tracks'],
  273. MEDIA_TYPE.VIDEO,
  274. id);
  275. // Is the video to be rendered?
  276. // FIXME It's currently impossible to have true as the value of
  277. // waitForVideoStarted because videoTrack's state videoStarted will be
  278. // updated only after videoTrack is rendered.
  279. // XXX Note that, unlike on web, we don't render video when the
  280. // connection status is interrupted, this is because the renderer
  281. // doesn't retain the last frame forever, so we would end up with a
  282. // black screen.
  283. const waitForVideoStarted = false;
  284. return !audioOnly
  285. && (connectionStatus
  286. === JitsiParticipantConnectionStatus.ACTIVE)
  287. && shouldRenderVideoTrack(videoTrack, waitForVideoStarted);
  288. }