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.

ThumbnailWrapper.js 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. /* @flow */
  2. import React, { Component } from 'react';
  3. import { shouldComponentUpdate } from 'react-window';
  4. import { getLocalParticipant } from '../../../base/participants';
  5. import { connect } from '../../../base/redux';
  6. import { getHideSelfView } from '../../../base/settings/functions.any';
  7. import { LAYOUTS, getCurrentLayout } from '../../../video-layout';
  8. import { FILMSTRIP_TYPE, TILE_ASPECT_RATIO, TILE_HORIZONTAL_MARGIN } from '../../constants';
  9. import { getActiveParticipantsIds, showGridInVerticalView } from '../../functions';
  10. import Thumbnail from './Thumbnail';
  11. /**
  12. * The type of the React {@code Component} props of {@link ThumbnailWrapper}.
  13. */
  14. type Props = {
  15. /**
  16. * Whether or not to hide the self view.
  17. */
  18. _disableSelfView: boolean,
  19. /**
  20. * The type of filmstrip this thumbnail is displayed in.
  21. */
  22. _filmstripType: string,
  23. /**
  24. * The horizontal offset in px for the thumbnail. Used to center the thumbnails in the last row in tile view.
  25. */
  26. _horizontalOffset: number,
  27. /**
  28. * The ID of the participant associated with the Thumbnail.
  29. */
  30. _participantID: ?string,
  31. /**
  32. * Whether or not the thumbnail is a local screen share.
  33. */
  34. _isLocalScreenShare: boolean,
  35. /**
  36. * The width of the thumbnail. Used for expanding the width of the thumbnails on last row in case
  37. * there is empty space.
  38. */
  39. _thumbnailWidth: number,
  40. /**
  41. * The index of the column in tile view.
  42. */
  43. columnIndex?: number,
  44. /**
  45. * The index of the ThumbnailWrapper in stage view.
  46. */
  47. index?: number,
  48. /**
  49. * The index of the row in tile view.
  50. */
  51. rowIndex?: number,
  52. /**
  53. * The styles coming from react-window.
  54. */
  55. style: Object
  56. };
  57. /**
  58. * A wrapper Component for the Thumbnail that translates the react-window specific props
  59. * to the Thumbnail Component's props.
  60. */
  61. class ThumbnailWrapper extends Component<Props> {
  62. /**
  63. * Creates new ThumbnailWrapper instance.
  64. *
  65. * @param {Props} props - The props of the component.
  66. */
  67. constructor(props: Props) {
  68. super(props);
  69. this.shouldComponentUpdate = shouldComponentUpdate.bind(this);
  70. }
  71. shouldComponentUpdate: Props => boolean;
  72. /**
  73. * Implements React's {@link Component#render()}.
  74. *
  75. * @inheritdoc
  76. * @returns {ReactElement}
  77. */
  78. render() {
  79. const {
  80. _disableSelfView,
  81. _filmstripType = FILMSTRIP_TYPE.MAIN,
  82. _isLocalScreenShare = false,
  83. _horizontalOffset = 0,
  84. _participantID,
  85. _thumbnailWidth,
  86. style
  87. } = this.props;
  88. if (typeof _participantID !== 'string') {
  89. return null;
  90. }
  91. if (_participantID === 'local') {
  92. return _disableSelfView ? null : (
  93. <Thumbnail
  94. filmstripType = { _filmstripType }
  95. horizontalOffset = { _horizontalOffset }
  96. key = 'local'
  97. style = { style }
  98. width = { _thumbnailWidth } />);
  99. }
  100. if (_isLocalScreenShare) {
  101. return _disableSelfView ? null : (
  102. <Thumbnail
  103. filmstripType = { _filmstripType }
  104. horizontalOffset = { _horizontalOffset }
  105. key = 'localScreenShare'
  106. participantID = { _participantID }
  107. style = { style }
  108. width = { _thumbnailWidth } />);
  109. }
  110. return (
  111. <Thumbnail
  112. filmstripType = { _filmstripType }
  113. horizontalOffset = { _horizontalOffset }
  114. key = { `remote_${_participantID}` }
  115. participantID = { _participantID }
  116. style = { style }
  117. width = { _thumbnailWidth } />);
  118. }
  119. }
  120. /**
  121. * Maps (parts of) the Redux state to the associated {@code ThumbnailWrapper}'s props.
  122. *
  123. * @param {Object} state - The Redux state.
  124. * @param {Object} ownProps - The props passed to the component.
  125. * @private
  126. * @returns {Props}
  127. */
  128. function _mapStateToProps(state, ownProps) {
  129. const _currentLayout = getCurrentLayout(state);
  130. const { remoteParticipants: remote } = state['features/filmstrip'];
  131. const activeParticipants = getActiveParticipantsIds(state);
  132. const disableSelfView = getHideSelfView(state);
  133. const _verticalViewGrid = showGridInVerticalView(state);
  134. const filmstripType = ownProps.data?.filmstripType;
  135. const stageFilmstrip = filmstripType === FILMSTRIP_TYPE.STAGE;
  136. const sortedActiveParticipants = activeParticipants.sort();
  137. const remoteParticipants = stageFilmstrip ? sortedActiveParticipants : remote;
  138. const remoteParticipantsLength = remoteParticipants.length;
  139. const localId = getLocalParticipant(state).id;
  140. if (_currentLayout === LAYOUTS.TILE_VIEW || _verticalViewGrid || stageFilmstrip) {
  141. const { columnIndex, rowIndex } = ownProps;
  142. const { tileViewDimensions, stageFilmstripDimensions, verticalViewDimensions } = state['features/filmstrip'];
  143. const { gridView } = verticalViewDimensions;
  144. let gridDimensions = tileViewDimensions.gridDimensions,
  145. thumbnailSize = tileViewDimensions.thumbnailSize;
  146. if (stageFilmstrip) {
  147. gridDimensions = stageFilmstripDimensions.gridDimensions;
  148. thumbnailSize = stageFilmstripDimensions.thumbnailSize;
  149. } else if (_verticalViewGrid) {
  150. gridDimensions = gridView.gridDimensions;
  151. thumbnailSize = gridView.thumbnailSize;
  152. }
  153. const { columns, rows } = gridDimensions;
  154. const index = (rowIndex * columns) + columnIndex;
  155. let horizontalOffset, thumbnailWidth;
  156. const { iAmRecorder, disableTileEnlargement } = state['features/base/config'];
  157. const { localScreenShare } = state['features/base/participants'];
  158. const localParticipantsLength = localScreenShare ? 2 : 1;
  159. let participantsLength;
  160. if (stageFilmstrip) {
  161. // We use the length of activeParticipants in stage filmstrip which includes local participants.
  162. participantsLength = remoteParticipantsLength;
  163. } else {
  164. // We need to include the local screenshare participant in tile view.
  165. participantsLength = remoteParticipantsLength
  166. // Add local camera and screen share to total participant count when self view is not disabled.
  167. + (disableSelfView ? 0 : localParticipantsLength)
  168. // Removes iAmRecorder from the total participants count.
  169. - (iAmRecorder ? 1 : 0);
  170. }
  171. if (rowIndex === rows - 1) { // center the last row
  172. const partialLastRowParticipantsNumber = participantsLength % columns;
  173. if (partialLastRowParticipantsNumber > 0) {
  174. const { width, height } = thumbnailSize;
  175. const availableWidth = columns * (width + TILE_HORIZONTAL_MARGIN);
  176. let widthDifference = 0;
  177. let widthToUse = width;
  178. if (!disableTileEnlargement) {
  179. thumbnailWidth = Math.min(
  180. (availableWidth / partialLastRowParticipantsNumber) - TILE_HORIZONTAL_MARGIN,
  181. height * TILE_ASPECT_RATIO);
  182. widthDifference = thumbnailWidth - width;
  183. widthToUse = thumbnailWidth;
  184. }
  185. horizontalOffset
  186. = Math.floor((availableWidth
  187. - (partialLastRowParticipantsNumber * (widthToUse + TILE_HORIZONTAL_MARGIN))) / 2
  188. )
  189. + (columnIndex * widthDifference);
  190. }
  191. }
  192. if (index > participantsLength - 1) {
  193. return {};
  194. }
  195. if (stageFilmstrip) {
  196. return {
  197. _disableSelfView: disableSelfView,
  198. _filmstripType: filmstripType,
  199. _participantID: remoteParticipants[index] === localId ? 'local' : remoteParticipants[index],
  200. _horizontalOffset: horizontalOffset,
  201. _thumbnailWidth: thumbnailWidth
  202. };
  203. }
  204. // When the thumbnails are reordered, local participant is inserted at index 0.
  205. const localIndex = disableSelfView ? remoteParticipantsLength : 0;
  206. // Local screen share is inserted at index 1 after the local camera.
  207. const localScreenShareIndex = disableSelfView ? remoteParticipantsLength : 1;
  208. const remoteIndex = !iAmRecorder && !disableSelfView
  209. ? index - localParticipantsLength
  210. : index;
  211. if (!iAmRecorder && index === localIndex) {
  212. return {
  213. _disableSelfView: disableSelfView,
  214. _filmstripType: filmstripType,
  215. _participantID: 'local',
  216. _horizontalOffset: horizontalOffset,
  217. _thumbnailWidth: thumbnailWidth
  218. };
  219. }
  220. if (!iAmRecorder && localScreenShare && index === localScreenShareIndex) {
  221. return {
  222. _disableSelfView: disableSelfView,
  223. _filmstripType: filmstripType,
  224. _isLocalScreenShare: true,
  225. _participantID: localScreenShare?.id,
  226. _horizontalOffset: horizontalOffset,
  227. _thumbnailWidth: thumbnailWidth
  228. };
  229. }
  230. return {
  231. _filmstripType: filmstripType,
  232. _participantID: remoteParticipants[remoteIndex],
  233. _horizontalOffset: horizontalOffset,
  234. _thumbnailWidth: thumbnailWidth
  235. };
  236. }
  237. if (_currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW && filmstripType === FILMSTRIP_TYPE.SCREENSHARE) {
  238. const { screenshareFilmstripParticipantId } = state['features/filmstrip'];
  239. const screenshares = state['features/video-layout'].remoteScreenShares;
  240. let id = screenshares.find(sId => sId === screenshareFilmstripParticipantId);
  241. if (!id && screenshares.length) {
  242. id = screenshares[screenshares.length - 1];
  243. }
  244. return {
  245. _filmstripType: filmstripType,
  246. _participantID: id
  247. };
  248. }
  249. const { index } = ownProps;
  250. if (typeof index !== 'number' || remoteParticipantsLength <= index) {
  251. return {};
  252. }
  253. return {
  254. _participantID: remoteParticipants[index]
  255. };
  256. }
  257. export default connect(_mapStateToProps)(ThumbnailWrapper);