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.

actions.web.js 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. // @flow
  2. import type { Dispatch } from 'redux';
  3. import { getSourceNameSignalingFeatureFlag } from '../base/config';
  4. import {
  5. getLocalParticipant,
  6. getParticipantById,
  7. getRemoteParticipantCount,
  8. pinParticipant
  9. } from '../base/participants';
  10. import { shouldHideSelfView } from '../base/settings/functions.any';
  11. import { getMaxColumnCount } from '../video-layout';
  12. import {
  13. ADD_STAGE_PARTICIPANT,
  14. REMOVE_STAGE_PARTICIPANT,
  15. SET_STAGE_PARTICIPANTS,
  16. SET_FILMSTRIP_WIDTH,
  17. SET_HORIZONTAL_VIEW_DIMENSIONS,
  18. SET_STAGE_FILMSTRIP_DIMENSIONS,
  19. SET_TILE_VIEW_DIMENSIONS,
  20. SET_USER_FILMSTRIP_WIDTH,
  21. SET_USER_IS_RESIZING,
  22. SET_VERTICAL_VIEW_DIMENSIONS,
  23. SET_VOLUME,
  24. SET_MAX_STAGE_PARTICIPANTS,
  25. TOGGLE_PIN_STAGE_PARTICIPANT
  26. } from './actionTypes';
  27. import {
  28. HORIZONTAL_FILMSTRIP_MARGIN,
  29. MAX_ACTIVE_PARTICIPANTS,
  30. SCROLL_SIZE,
  31. STAGE_VIEW_THUMBNAIL_VERTICAL_BORDER,
  32. TILE_HORIZONTAL_MARGIN,
  33. TILE_VERTICAL_CONTAINER_HORIZONTAL_MARGIN,
  34. TILE_VERTICAL_MARGIN,
  35. TILE_VIEW_DEFAULT_NUMBER_OF_VISIBLE_TILES,
  36. TILE_VIEW_GRID_HORIZONTAL_MARGIN,
  37. TILE_VIEW_GRID_VERTICAL_MARGIN,
  38. VERTICAL_FILMSTRIP_VERTICAL_MARGIN
  39. } from './constants';
  40. import {
  41. calculateNonResponsiveTileViewDimensions,
  42. calculateResponsiveTileViewDimensions,
  43. calculateThumbnailSizeForHorizontalView,
  44. calculateThumbnailSizeForVerticalView,
  45. getNumberOfPartipantsForTileView,
  46. getVerticalViewMaxWidth,
  47. isFilmstripResizable,
  48. showGridInVerticalView
  49. } from './functions';
  50. export * from './actions.any';
  51. /**
  52. * Sets the dimensions of the tile view grid.
  53. *
  54. * @returns {Function}
  55. */
  56. export function setTileViewDimensions() {
  57. return (dispatch: Dispatch<any>, getState: Function) => {
  58. const state = getState();
  59. const { clientHeight, clientWidth } = state['features/base/responsive-ui'];
  60. const {
  61. disableResponsiveTiles,
  62. disableTileEnlargement,
  63. tileView = {}
  64. } = state['features/base/config'];
  65. const { numberOfVisibleTiles = TILE_VIEW_DEFAULT_NUMBER_OF_VISIBLE_TILES } = tileView;
  66. const numberOfParticipants = getNumberOfPartipantsForTileView(state);
  67. const maxColumns = getMaxColumnCount(state);
  68. const {
  69. height,
  70. width,
  71. columns,
  72. rows
  73. } = disableResponsiveTiles
  74. ? calculateNonResponsiveTileViewDimensions(state)
  75. : calculateResponsiveTileViewDimensions({
  76. clientWidth,
  77. clientHeight,
  78. disableTileEnlargement,
  79. maxColumns,
  80. numberOfParticipants,
  81. desiredNumberOfVisibleTiles: numberOfVisibleTiles
  82. });
  83. const thumbnailsTotalHeight = rows * (TILE_VERTICAL_MARGIN + height);
  84. const availableHeight = clientHeight - TILE_VIEW_GRID_VERTICAL_MARGIN;
  85. const hasScroll = availableHeight < thumbnailsTotalHeight;
  86. const filmstripWidth
  87. = Math.min(clientWidth - TILE_VIEW_GRID_HORIZONTAL_MARGIN, columns * (TILE_HORIZONTAL_MARGIN + width))
  88. + (hasScroll ? SCROLL_SIZE : 0);
  89. const filmstripHeight = Math.min(availableHeight, thumbnailsTotalHeight);
  90. dispatch({
  91. type: SET_TILE_VIEW_DIMENSIONS,
  92. dimensions: {
  93. gridDimensions: {
  94. columns,
  95. rows
  96. },
  97. thumbnailSize: {
  98. height,
  99. width
  100. },
  101. filmstripHeight,
  102. filmstripWidth,
  103. hasScroll
  104. }
  105. });
  106. };
  107. }
  108. /**
  109. * Sets the dimensions of the thumbnails in vertical view.
  110. *
  111. * @returns {Function}
  112. */
  113. export function setVerticalViewDimensions() {
  114. return (dispatch: Dispatch<any>, getState: Function) => {
  115. const state = getState();
  116. const { clientHeight = 0, clientWidth = 0 } = state['features/base/responsive-ui'];
  117. const { width: filmstripWidth } = state['features/filmstrip'];
  118. const disableSelfView = shouldHideSelfView(state);
  119. const resizableFilmstrip = isFilmstripResizable(state);
  120. const _verticalViewGrid = showGridInVerticalView(state);
  121. const numberOfRemoteParticipants = getRemoteParticipantCount(state);
  122. const { localScreenShare } = state['features/base/participants'];
  123. let gridView = {};
  124. let thumbnails = {};
  125. let filmstripDimensions = {};
  126. let hasScroll = false;
  127. let remoteVideosContainerWidth;
  128. let remoteVideosContainerHeight;
  129. // grid view in the vertical filmstrip
  130. if (_verticalViewGrid) {
  131. const { tileView = {} } = state['features/base/config'];
  132. const { numberOfVisibleTiles = TILE_VIEW_DEFAULT_NUMBER_OF_VISIBLE_TILES } = tileView;
  133. const numberOfParticipants = getNumberOfPartipantsForTileView(state);
  134. const maxColumns = getMaxColumnCount(state, {
  135. width: filmstripWidth.current,
  136. disableResponsiveTiles: false,
  137. disableTileEnlargement: false
  138. });
  139. const {
  140. height,
  141. width,
  142. columns,
  143. rows
  144. } = calculateResponsiveTileViewDimensions({
  145. clientWidth: filmstripWidth.current,
  146. clientHeight,
  147. disableTileEnlargement: false,
  148. maxColumns,
  149. noHorizontalContainerMargin: true,
  150. numberOfParticipants,
  151. desiredNumberOfVisibleTiles: numberOfVisibleTiles
  152. });
  153. const thumbnailsTotalHeight = rows * (TILE_VERTICAL_MARGIN + height);
  154. hasScroll = clientHeight < thumbnailsTotalHeight;
  155. const widthOfFilmstrip = (columns * (TILE_HORIZONTAL_MARGIN + width)) + (hasScroll ? SCROLL_SIZE : 0);
  156. const filmstripHeight = Math.min(clientHeight - TILE_VIEW_GRID_VERTICAL_MARGIN, thumbnailsTotalHeight);
  157. gridView = {
  158. gridDimensions: {
  159. columns,
  160. rows
  161. },
  162. thumbnailSize: {
  163. height,
  164. width
  165. },
  166. hasScroll
  167. };
  168. filmstripDimensions = {
  169. height: filmstripHeight,
  170. width: widthOfFilmstrip
  171. };
  172. } else {
  173. thumbnails = calculateThumbnailSizeForVerticalView(clientWidth, filmstripWidth.current, resizableFilmstrip);
  174. remoteVideosContainerWidth
  175. = thumbnails?.local?.width + TILE_VERTICAL_CONTAINER_HORIZONTAL_MARGIN + SCROLL_SIZE;
  176. remoteVideosContainerHeight
  177. = clientHeight - (disableSelfView ? 0 : thumbnails?.local?.height) - VERTICAL_FILMSTRIP_VERTICAL_MARGIN;
  178. if (getSourceNameSignalingFeatureFlag(state)) {
  179. // Account for the height of the local screen share thumbnail when calculating the height of the remote
  180. // videos container.
  181. const localCameraThumbnailHeight = thumbnails?.local?.height;
  182. const localScreenShareThumbnailHeight
  183. = localScreenShare && !disableSelfView ? thumbnails?.local?.height : 0;
  184. remoteVideosContainerHeight = clientHeight
  185. - localCameraThumbnailHeight
  186. - localScreenShareThumbnailHeight
  187. - VERTICAL_FILMSTRIP_VERTICAL_MARGIN;
  188. }
  189. hasScroll
  190. = remoteVideosContainerHeight
  191. < (thumbnails?.remote.height + TILE_VERTICAL_MARGIN) * numberOfRemoteParticipants;
  192. }
  193. dispatch({
  194. type: SET_VERTICAL_VIEW_DIMENSIONS,
  195. dimensions: {
  196. ...thumbnails,
  197. remoteVideosContainer: _verticalViewGrid ? filmstripDimensions : {
  198. width: remoteVideosContainerWidth,
  199. height: remoteVideosContainerHeight
  200. },
  201. gridView,
  202. hasScroll
  203. }
  204. });
  205. };
  206. }
  207. /**
  208. * Sets the dimensions of the thumbnails in horizontal view.
  209. *
  210. * @returns {Function}
  211. */
  212. export function setHorizontalViewDimensions() {
  213. return (dispatch: Dispatch<any>, getState: Function) => {
  214. const state = getState();
  215. const { clientHeight = 0, clientWidth = 0 } = state['features/base/responsive-ui'];
  216. const disableSelfView = shouldHideSelfView(state);
  217. const thumbnails = calculateThumbnailSizeForHorizontalView(clientHeight);
  218. const remoteVideosContainerWidth
  219. = clientWidth - (disableSelfView ? 0 : thumbnails?.local?.width) - HORIZONTAL_FILMSTRIP_MARGIN;
  220. const remoteVideosContainerHeight
  221. = thumbnails?.local?.height + TILE_VERTICAL_MARGIN + STAGE_VIEW_THUMBNAIL_VERTICAL_BORDER + SCROLL_SIZE;
  222. const numberOfRemoteParticipants = getRemoteParticipantCount(state);
  223. const hasScroll
  224. = remoteVideosContainerHeight
  225. < (thumbnails?.remote.width + TILE_HORIZONTAL_MARGIN) * numberOfRemoteParticipants;
  226. dispatch({
  227. type: SET_HORIZONTAL_VIEW_DIMENSIONS,
  228. dimensions: {
  229. ...thumbnails,
  230. remoteVideosContainer: {
  231. width: remoteVideosContainerWidth,
  232. height: remoteVideosContainerHeight
  233. },
  234. hasScroll
  235. }
  236. });
  237. };
  238. }
  239. /**
  240. * Sets the dimensions of the stage filmstrip tile view grid.
  241. *
  242. * @returns {Function}
  243. */
  244. export function setStageFilmstripViewDimensions() {
  245. return (dispatch: Dispatch<any>, getState: Function) => {
  246. const state = getState();
  247. const { clientHeight, clientWidth } = state['features/base/responsive-ui'];
  248. const {
  249. disableResponsiveTiles,
  250. disableTileEnlargement,
  251. tileView = {}
  252. } = state['features/base/config'];
  253. const { visible } = state['features/filmstrip'];
  254. const verticalWidth = visible ? getVerticalViewMaxWidth(state) : 0;
  255. const { numberOfVisibleTiles = MAX_ACTIVE_PARTICIPANTS } = tileView;
  256. const numberOfParticipants = state['features/filmstrip'].activeParticipants.length;
  257. const availableWidth = clientWidth - verticalWidth;
  258. const maxColumns = getMaxColumnCount(state, { width: availableWidth });
  259. const {
  260. height,
  261. width,
  262. columns,
  263. rows
  264. } = disableResponsiveTiles
  265. ? calculateNonResponsiveTileViewDimensions(state, true)
  266. : calculateResponsiveTileViewDimensions({
  267. clientWidth: availableWidth,
  268. clientHeight,
  269. disableTileEnlargement,
  270. maxColumns,
  271. noHorizontalContainerMargin: verticalWidth > 0,
  272. numberOfParticipants,
  273. numberOfVisibleTiles
  274. });
  275. const thumbnailsTotalHeight = rows * (TILE_VERTICAL_MARGIN + height);
  276. const hasScroll = clientHeight < thumbnailsTotalHeight;
  277. const filmstripWidth
  278. = Math.min(clientWidth - TILE_VIEW_GRID_HORIZONTAL_MARGIN, columns * (TILE_HORIZONTAL_MARGIN + width))
  279. + (hasScroll ? SCROLL_SIZE : 0);
  280. const filmstripHeight = Math.min(clientHeight - TILE_VIEW_GRID_VERTICAL_MARGIN, thumbnailsTotalHeight);
  281. dispatch({
  282. type: SET_STAGE_FILMSTRIP_DIMENSIONS,
  283. dimensions: {
  284. gridDimensions: {
  285. columns,
  286. rows
  287. },
  288. thumbnailSize: {
  289. height,
  290. width
  291. },
  292. filmstripHeight,
  293. filmstripWidth,
  294. hasScroll
  295. }
  296. });
  297. };
  298. }
  299. /**
  300. * Emulates a click on the n-th video.
  301. *
  302. * @param {number} n - Number that identifies the video.
  303. * @returns {Function}
  304. */
  305. export function clickOnVideo(n: number) {
  306. return (dispatch: Function, getState: Function) => {
  307. const state = getState();
  308. const { id: localId } = getLocalParticipant(state);
  309. // Use the list that correctly represents the current order of the participants as visible in the UI.
  310. const { remoteParticipants } = state['features/filmstrip'];
  311. const participants = [ localId, ...remoteParticipants ];
  312. const { id, pinned } = getParticipantById(state, participants[n]);
  313. dispatch(pinParticipant(pinned ? null : id));
  314. };
  315. }
  316. /**
  317. * Sets the volume for a thumbnail's audio.
  318. *
  319. * @param {string} participantId - The participant ID asociated with the audio.
  320. * @param {string} volume - The volume level.
  321. * @returns {{
  322. * type: SET_VOLUME,
  323. * participantId: string,
  324. * volume: number
  325. * }}
  326. */
  327. export function setVolume(participantId: string, volume: number) {
  328. return {
  329. type: SET_VOLUME,
  330. participantId,
  331. volume
  332. };
  333. }
  334. /**
  335. * Sets the filmstrip's width.
  336. *
  337. * @param {number} width - The new width of the filmstrip.
  338. * @returns {{
  339. * type: SET_FILMSTRIP_WIDTH,
  340. * width: number
  341. * }}
  342. */
  343. export function setFilmstripWidth(width: number) {
  344. return {
  345. type: SET_FILMSTRIP_WIDTH,
  346. width
  347. };
  348. }
  349. /**
  350. * Sets the filmstrip's width and the user preferred width.
  351. *
  352. * @param {number} width - The new width of the filmstrip.
  353. * @returns {{
  354. * type: SET_USER_FILMSTRIP_WIDTH,
  355. * width: number
  356. * }}
  357. */
  358. export function setUserFilmstripWidth(width: number) {
  359. return {
  360. type: SET_USER_FILMSTRIP_WIDTH,
  361. width
  362. };
  363. }
  364. /**
  365. * Sets whether the user is resizing or not.
  366. *
  367. * @param {boolean} resizing - Whether the user is resizing or not.
  368. * @returns {Object}
  369. */
  370. export function setUserIsResizing(resizing: boolean) {
  371. return {
  372. type: SET_USER_IS_RESIZING,
  373. resizing
  374. };
  375. }
  376. /**
  377. * Add participant to the active participants list.
  378. *
  379. * @param {string} participantId - The Id of the participant to be added.
  380. * @param {boolean?} pinned - Whether the participant is pinned or not.
  381. * @returns {Object}
  382. */
  383. export function addStageParticipant(participantId, pinned = false) {
  384. return {
  385. type: ADD_STAGE_PARTICIPANT,
  386. participantId,
  387. pinned
  388. };
  389. }
  390. /**
  391. * Remove participant from the active participants list.
  392. *
  393. * @param {string} participantId - The Id of the participant to be removed.
  394. * @returns {Object}
  395. */
  396. export function removeStageParticipant(participantId) {
  397. return {
  398. type: REMOVE_STAGE_PARTICIPANT,
  399. participantId
  400. };
  401. }
  402. /**
  403. * Sets the active participants list.
  404. *
  405. * @param {Array<Object>} queue - The new list.
  406. * @returns {Object}
  407. */
  408. export function setStageParticipants(queue) {
  409. return {
  410. type: SET_STAGE_PARTICIPANTS,
  411. queue
  412. };
  413. }
  414. /**
  415. * Sets the max number of participants to be displayed on stage.
  416. *
  417. * @param {number} maxParticipants - Max number of participants.
  418. * @returns {Object}
  419. */
  420. export function setMaxStageParticipants(maxParticipants) {
  421. return {
  422. type: SET_MAX_STAGE_PARTICIPANTS,
  423. maxParticipants
  424. };
  425. }
  426. /**
  427. * Toggles the pin state of the given participant.
  428. *
  429. * @param {string} participantId - The id of the participant to be toggled.
  430. * @returns {Object}
  431. */
  432. export function togglePinStageParticipant(participantId) {
  433. return {
  434. type: TOGGLE_PIN_STAGE_PARTICIPANT,
  435. participantId
  436. };
  437. }