123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117 |
- // @flow
-
- import _ from 'lodash';
- import React, { useCallback, useRef, useState } from 'react';
- import { useTranslation } from 'react-i18next';
- import { useSelector, useDispatch } from 'react-redux';
-
- import { openDialog } from '../../base/dialog';
- import { getParticipants } from '../../base/participants';
- import MuteRemoteParticipantDialog from '../../video-menu/components/web/MuteRemoteParticipantDialog';
- import { findStyledAncestor, shouldRenderInviteButton } from '../functions';
-
- import { InviteButton } from './InviteButton';
- import { MeetingParticipantContextMenu } from './MeetingParticipantContextMenu';
- import { MeetingParticipantItem } from './MeetingParticipantItem';
- import { Heading, ParticipantContainer } from './styled';
-
- type NullProto = {
- [key: string]: any,
- __proto__: null
- };
-
- type RaiseContext = NullProto | {
-
- /**
- * Target elements against which positioning calculations are made
- */
- offsetTarget?: HTMLElement,
-
- /**
- * Participant reference
- */
- participant?: Object,
- };
-
- const initialState = Object.freeze(Object.create(null));
-
- export const MeetingParticipantList = () => {
- const dispatch = useDispatch();
- const isMouseOverMenu = useRef(false);
- const participants = useSelector(getParticipants, _.isEqual);
- const showInviteButton = useSelector(shouldRenderInviteButton);
- const [ raiseContext, setRaiseContext ] = useState<RaiseContext>(initialState);
- const { t } = useTranslation();
-
- const lowerMenu = useCallback(() => {
- /**
- * We are tracking mouse movement over the active participant item and
- * the context menu. Due to the order of enter/leave events, we need to
- * defer checking if the mouse is over the context menu with
- * queueMicrotask
- */
- window.queueMicrotask(() => {
- if (isMouseOverMenu.current) {
- return;
- }
-
- if (raiseContext !== initialState) {
- setRaiseContext(initialState);
- }
- });
- }, [ raiseContext ]);
-
- const raiseMenu = useCallback((participant, target) => {
- setRaiseContext({
- participant,
- offsetTarget: findStyledAncestor(target, ParticipantContainer)
- });
- }, [ raiseContext ]);
-
- const toggleMenu = useCallback(participant => e => {
- const { participant: raisedParticipant } = raiseContext;
-
- if (raisedParticipant && raisedParticipant === participant) {
- lowerMenu();
- } else {
- raiseMenu(participant, e.target);
- }
- }, [ raiseContext ]);
-
- const menuEnter = useCallback(() => {
- isMouseOverMenu.current = true;
- }, []);
-
- const menuLeave = useCallback(() => {
- isMouseOverMenu.current = false;
- lowerMenu();
- }, [ lowerMenu ]);
-
- const muteAudio = useCallback(id => () => {
- dispatch(openDialog(MuteRemoteParticipantDialog, { participantID: id }));
- });
-
- return (
- <>
- <Heading>{t('participantsPane.headings.participantsList', { count: participants.length })}</Heading>
- {showInviteButton && <InviteButton />}
- <div>
- {participants.map(p => (
- <MeetingParticipantItem
- isHighlighted = { raiseContext.participant === p }
- key = { p.id }
- muteAudio = { muteAudio }
- onContextMenu = { toggleMenu(p) }
- onLeave = { lowerMenu }
- participant = { p } />
- ))}
- </div>
- <MeetingParticipantContextMenu
- muteAudio = { muteAudio }
- onEnter = { menuEnter }
- onLeave = { menuLeave }
- onSelect = { lowerMenu }
- { ...raiseContext } />
- </>
- );
- };
|