Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

ThumbnailWrapper.tsx 11KB

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