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.

ParticipantsPane.js 3.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. // @flow
  2. import React, { useCallback, useEffect, useState } from 'react';
  3. import { useTranslation } from 'react-i18next';
  4. import { useDispatch, useSelector } from 'react-redux';
  5. import { ThemeProvider } from 'styled-components';
  6. import { openDialog } from '../../base/dialog';
  7. import {
  8. getParticipantCount,
  9. isEveryoneModerator,
  10. isLocalParticipantModerator
  11. } from '../../base/participants';
  12. import { MuteEveryoneDialog } from '../../video-menu/components/';
  13. import { close } from '../actions';
  14. import { classList, findStyledAncestor, getParticipantsPaneOpen } from '../functions';
  15. import theme from '../theme.json';
  16. import { FooterContextMenu } from './FooterContextMenu';
  17. import { LobbyParticipantList } from './LobbyParticipantList';
  18. import { MeetingParticipantList } from './MeetingParticipantList';
  19. import {
  20. AntiCollapse,
  21. Close,
  22. Container,
  23. Footer,
  24. FooterButton,
  25. FooterEllipsisButton,
  26. FooterEllipsisContainer,
  27. Header
  28. } from './styled';
  29. export const ParticipantsPane = () => {
  30. const dispatch = useDispatch();
  31. const paneOpen = useSelector(getParticipantsPaneOpen);
  32. const isLocalModerator = useSelector(isLocalParticipantModerator);
  33. const participantsCount = useSelector(getParticipantCount);
  34. const everyoneModerator = useSelector(isEveryoneModerator);
  35. const showContextMenu = !everyoneModerator && participantsCount > 2;
  36. const [ contextOpen, setContextOpen ] = useState(false);
  37. const { t } = useTranslation();
  38. const closePane = useCallback(() => dispatch(close(), [ dispatch ]));
  39. const closePaneKeyPress = useCallback(e => {
  40. if (closePane && (e.key === ' ' || e.key === 'Enter')) {
  41. e.preventDefault();
  42. closePane();
  43. }
  44. }, [ closePane ]);
  45. const muteAll = useCallback(() => dispatch(openDialog(MuteEveryoneDialog)), [ dispatch ]);
  46. useEffect(() => {
  47. const handler = [ 'click', e => {
  48. if (!findStyledAncestor(e.target, FooterEllipsisContainer)) {
  49. setContextOpen(false);
  50. }
  51. } ];
  52. window.addEventListener(...handler);
  53. return () => window.removeEventListener(...handler);
  54. }, [ contextOpen ]);
  55. const toggleContext = useCallback(() => setContextOpen(!contextOpen), [ contextOpen, setContextOpen ]);
  56. return (
  57. <ThemeProvider theme = { theme }>
  58. <div className = { classList('participants_pane', !paneOpen && 'participants_pane--closed') }>
  59. <div className = 'participants_pane-content'>
  60. <Header>
  61. <Close
  62. aria-label = { t('participantsPane.close', 'Close') }
  63. onClick = { closePane }
  64. onKeyPress = { closePaneKeyPress }
  65. role = 'button'
  66. tabIndex = { 0 } />
  67. </Header>
  68. <Container>
  69. <LobbyParticipantList />
  70. <AntiCollapse />
  71. <MeetingParticipantList />
  72. </Container>
  73. {isLocalModerator && (
  74. <Footer>
  75. <FooterButton onClick = { muteAll }>
  76. {t('participantsPane.actions.muteAll')}
  77. </FooterButton>
  78. {showContextMenu && (
  79. <FooterEllipsisContainer>
  80. <FooterEllipsisButton
  81. id = 'participants-pane-context-menu'
  82. onClick = { toggleContext } />
  83. {contextOpen && <FooterContextMenu onMouseLeave = { toggleContext } />}
  84. </FooterEllipsisContainer>
  85. )}
  86. </Footer>
  87. )}
  88. </div>
  89. </div>
  90. </ThemeProvider>
  91. );
  92. };