import React, { useMemo, useState, useCallback, useEffect, ChangeEventHandler, } from 'react'; import { useHMSTheme } from '../../hooks/HMSThemeProvider'; import { CloseIcon, DownCaratIcon, SettingsIcon, UpCaratIcon } from '../Icons'; import { ParticipantInList } from './ParticipantInList'; import { hmsUiClassParserGenerator } from '../../utils/classes'; import './index.css'; import ClickAwayListener from 'react-click-away-listener'; import { HMSPeer, HMSTrack, selectIsConnectedToRoom, selectLocalPeerRole, selectPeerCount, selectPeers, selectRemotePeers, } from '@100mslive/hms-video-store'; import { useHMSActions, useHMSStore } from '../../hooks/HMSRoomProvider'; import { ParticipantListClasses, ParticipantListProps, } from './ParticipantProps'; import { Text } from '../Text'; import { selectAvailableRoleNames } from '@100mslive/hms-video-store'; import { Button } from '../Button'; import { HMSDialog } from '../Dialog'; import { groupBy } from '../../utils'; const defaultClasses: ParticipantListClasses = { root: 'flex flex-grow border-opacity-0 sm:hidden md:inline-block relative', buttonRoot: 'text-gray-300 dark:text-gray-500 flex border-opacity-0 focus:outline-none w-31 md:w-31 py-0.5 my-2 bg-white', buttonOpen: 'rounded-t-xl rounded-b-xl dark:bg-gray-100 shadow-1 dark:shadow-none', buttonClosed: 'rounded-xl dark:bg-black', buttonInner: 'flex flex-grow justify-end md:justify-center px-3 m-0 my-1 tracking-wide self-center', buttonText: 'pl-2 self-center', carat: 'w-3 h-3', // TODO fix shadow border menuRoot: 'w-52 md:w-60 max-h-116 pb-2 overflow-y-auto rounded-t-xl rounded-b-xl bg-white shadow-1 dark:shadow-none dark:bg-gray-100 focus:outline-none z-50 absolute right-0', menuSection: 'text-gray-200 dark:text-gray-500 group flex items-center px-3 pt-3 pb-2 text-base', menuItem: 'text-gray-100 dark:text-white group flex items-center flex-nowrap px-3 py-2 text-base hover:bg-gray-600 dark:hover:bg-gray-200', menuText: 'flex-1 flex items-center min-w-0', menuIconContainer: 'w-20 flex flex-shrink-0 justify-self-end justify-end', onIcon: '', offIcon: '', dialogContainer: 'bg-white text-gray-100 dark:bg-gray-100 dark:text-white w-full md:w-96 p-4 rounded-xl', dialogHeader: 'flex items-center space-x-2', expanded: 'flex-1 min-w-0 truncate', textGray: 'text-gray-400', divider: 'bg-gray-600 dark:bg-gray-200 h-px w-full my-4', formContainer: 'px-4 py-2 space-y-2', formRow: 'flex space-x-2 items-center justify-center', selectContainer: 'rounded-lg bg-gray-600 dark:bg-gray-200 p-2 mx-2', select: 'rounded-lg w-full h-full bg-gray-600 dark:bg-gray-200 focus:outline-none', dialogFooter: 'flex justify-end', checkBoxLabel: 'text-sm space-x-1 flex items-center', }; const customClasses: ParticipantListClasses = { menuRoot: 'hmsui-participantList-scrollbar', onIcon: 'hmsui-participantList-show-on-group-hover', }; const List = ({ isPreview = false, styler, participantInListProps, }: { isPreview: boolean; styler: (s: keyof ParticipantListClasses) => string | undefined; participantInListProps?: ( peer: HMSPeer, track?: HMSTrack, ) => Record; }) => { const { toast } = useHMSTheme(); const isConnected = useHMSStore(selectIsConnectedToRoom); const participantList = useHMSStore( isConnected ? selectPeers : selectRemotePeers, ); const roleNames = useHMSStore(selectAvailableRoleNames); const roles = groupBy(participantList); const hmsActions = useHMSActions(); const localPeerRole = useHMSStore(selectLocalPeerRole); const [selectedPeer, setSelectedPeer] = useState(null); const [selectedRole, setSelectedRole] = useState(''); const [forceChange, setForceChange] = useState(false); const handleRoleChangeClose = () => { setSelectedPeer(null); setForceChange(false); }; const handleInputChange: ChangeEventHandler = event => { setSelectedRole(event.currentTarget.value); }; const handleSaveSettings = async () => { if ( !selectedPeer || !selectedRole || !localPeerRole?.permissions?.changeRole ) { return; } if (selectedPeer.roleName !== selectedRole) { try { await hmsActions.changeRole(selectedPeer.id, selectedRole, forceChange); } catch (error) { toast((error as Error).message); } } setSelectedPeer(null); setForceChange(false); }; return ( <>
User Settings ({selectedPeer?.name})
{roles.map(([role, peers]) => (
{role === 'undefined' ? 'Unknown' : role}({peers.length})
{peers.map(peer => { const additionalProps = participantInListProps ? participantInListProps(peer) : {}; return ( { setSelectedPeer(peer); setSelectedRole(peer.roleName || ''); }} {...additionalProps} /> ); })}
))}
); }; export const ParticipantList = ({ isPreview = false, classes, onToggle, participantInListProps, }: ParticipantListProps) => { const { tw } = useHMSTheme(); const styler = useMemo( () => hmsUiClassParserGenerator({ tw, classes, customClasses, defaultClasses, tag: 'hmsui-participantList', }), [], ); const peerCount = useHMSStore(selectPeerCount); const [listOpen, setListOpen] = useState(false); const handleClick = useCallback(() => setListOpen(open => !open), []); const handleClose = useCallback(() => setListOpen(false), []); useEffect(() => { if (onToggle) { onToggle(listOpen); } }, [listOpen]); return (
{listOpen && ( )}
); };