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 14KB

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