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

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