Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

ParticipantsPane.tsx 7.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. import React, { useCallback, useEffect, useState } from 'react';
  2. import { useTranslation } from 'react-i18next';
  3. import { useDispatch, useSelector } from 'react-redux';
  4. import { makeStyles } from 'tss-react/mui';
  5. import { IReduxState } from '../../../app/types';
  6. import participantsPaneTheme from '../../../base/components/themes/participantsPaneTheme.json';
  7. import { openDialog } from '../../../base/dialog/actions';
  8. import { IconCloseLarge, IconDotsHorizontal } from '../../../base/icons/svg';
  9. import { isLocalParticipantModerator } from '../../../base/participants/functions';
  10. import Button from '../../../base/ui/components/web/Button';
  11. import ClickableIcon from '../../../base/ui/components/web/ClickableIcon';
  12. import { BUTTON_TYPES } from '../../../base/ui/constants.web';
  13. import { findAncestorByClass } from '../../../base/ui/functions.web';
  14. import { isAddBreakoutRoomButtonVisible } from '../../../breakout-rooms/functions';
  15. import MuteEveryoneDialog from '../../../video-menu/components/web/MuteEveryoneDialog';
  16. import { close } from '../../actions.web';
  17. import {
  18. getParticipantsPaneOpen,
  19. isMoreActionsVisible,
  20. isMuteAllVisible
  21. } from '../../functions';
  22. import { AddBreakoutRoomButton } from '../breakout-rooms/components/web/AddBreakoutRoomButton';
  23. // eslint-disable-next-line lines-around-comment
  24. // @ts-ignore
  25. import { RoomList } from '../breakout-rooms/components/web/RoomList';
  26. import { FooterContextMenu } from './FooterContextMenu';
  27. import LobbyParticipants from './LobbyParticipants';
  28. import MeetingParticipants from './MeetingParticipants';
  29. const useStyles = makeStyles()(theme => {
  30. return {
  31. container: {
  32. boxSizing: 'border-box' as const,
  33. flex: 1,
  34. overflowY: 'auto' as const,
  35. position: 'relative' as const,
  36. padding: `0 ${participantsPaneTheme.panePadding}px`,
  37. [`& > * + *:not(.${participantsPaneTheme.ignoredChildClassName})`]: {
  38. marginTop: theme.spacing(3)
  39. },
  40. '&::-webkit-scrollbar': {
  41. display: 'none'
  42. }
  43. },
  44. closeButton: {
  45. alignItems: 'center',
  46. cursor: 'pointer',
  47. display: 'flex',
  48. justifyContent: 'center'
  49. },
  50. header: {
  51. alignItems: 'center',
  52. boxSizing: 'border-box' as const,
  53. display: 'flex',
  54. height: `${participantsPaneTheme.headerSize}px`,
  55. padding: '0 20px',
  56. justifyContent: 'flex-end'
  57. },
  58. antiCollapse: {
  59. fontSize: 0,
  60. '&:first-child': {
  61. display: 'none'
  62. },
  63. '&:first-child + *': {
  64. marginTop: 0
  65. }
  66. },
  67. footer: {
  68. display: 'flex',
  69. justifyContent: 'flex-end',
  70. padding: `${theme.spacing(4)} ${participantsPaneTheme.panePadding}px`,
  71. '& > *:not(:last-child)': {
  72. marginRight: theme.spacing(3)
  73. }
  74. },
  75. footerMoreContainer: {
  76. position: 'relative' as const
  77. }
  78. };
  79. });
  80. const ParticipantsPane = () => {
  81. const { classes } = useStyles();
  82. const paneOpen = useSelector(getParticipantsPaneOpen);
  83. const isBreakoutRoomsSupported = useSelector((state: IReduxState) => state['features/base/conference'])
  84. .conference?.getBreakoutRooms()?.isSupported();
  85. const showAddRoomButton = useSelector(isAddBreakoutRoomButtonVisible);
  86. const showFooter = useSelector(isLocalParticipantModerator);
  87. const showMuteAllButton = useSelector(isMuteAllVisible);
  88. const showMoreActionsButton = useSelector(isMoreActionsVisible);
  89. const dispatch = useDispatch();
  90. const { t } = useTranslation();
  91. const [ contextOpen, setContextOpen ] = useState(false);
  92. const [ searchString, setSearchString ] = useState('');
  93. const onWindowClickListener = useCallback((e: any) => {
  94. if (contextOpen && !findAncestorByClass(e.target, classes.footerMoreContainer)) {
  95. setContextOpen(false);
  96. }
  97. }, [ contextOpen ]);
  98. useEffect(() => {
  99. window.addEventListener('click', onWindowClickListener);
  100. return () => {
  101. window.removeEventListener('click', onWindowClickListener);
  102. };
  103. }, []);
  104. const onClosePane = useCallback(() => {
  105. dispatch(close());
  106. }, []);
  107. const onDrawerClose = useCallback(() => {
  108. setContextOpen(false);
  109. }, []);
  110. const onMuteAll = useCallback(() => {
  111. dispatch(openDialog(MuteEveryoneDialog));
  112. }, []);
  113. const onToggleContext = useCallback(() => {
  114. setContextOpen(open => !open);
  115. }, []);
  116. if (!paneOpen) {
  117. return null;
  118. }
  119. return (
  120. <div className = 'participants_pane'>
  121. <div className = 'participants_pane-content'>
  122. <div className = { classes.header }>
  123. <ClickableIcon
  124. accessibilityLabel = { t('participantsPane.close', 'Close') }
  125. icon = { IconCloseLarge }
  126. onClick = { onClosePane } />
  127. </div>
  128. <div className = { classes.container }>
  129. <LobbyParticipants />
  130. <br className = { classes.antiCollapse } />
  131. <MeetingParticipants
  132. searchString = { searchString }
  133. setSearchString = { setSearchString } />
  134. {isBreakoutRoomsSupported && <RoomList searchString = { searchString } />}
  135. {showAddRoomButton && <AddBreakoutRoomButton />}
  136. </div>
  137. {showFooter && (
  138. <div className = { classes.footer }>
  139. {showMuteAllButton && (
  140. <Button
  141. accessibilityLabel = { t('participantsPane.actions.muteAll') }
  142. labelKey = { 'participantsPane.actions.muteAll' }
  143. onClick = { onMuteAll }
  144. type = { BUTTON_TYPES.SECONDARY } />
  145. )}
  146. {showMoreActionsButton && (
  147. <div className = { classes.footerMoreContainer }>
  148. <Button
  149. accessibilityLabel = { t('participantsPane.actions.moreModerationActions') }
  150. icon = { IconDotsHorizontal }
  151. id = 'participants-pane-context-menu'
  152. onClick = { onToggleContext }
  153. type = { BUTTON_TYPES.SECONDARY } />
  154. <FooterContextMenu
  155. isOpen = { contextOpen }
  156. onDrawerClose = { onDrawerClose }
  157. onMouseLeave = { onToggleContext } />
  158. </div>
  159. )}
  160. </div>
  161. )}
  162. </div>
  163. </div>
  164. );
  165. };
  166. export default ParticipantsPane;