diff --git a/packages/roomkit-react/src/Prebuilt/components/Footer/ParticipantList.tsx b/packages/roomkit-react/src/Prebuilt/components/Footer/ParticipantList.tsx index a72f4f4fc8..91b8f3a2c0 100644 --- a/packages/roomkit-react/src/Prebuilt/components/Footer/ParticipantList.tsx +++ b/packages/roomkit-react/src/Prebuilt/components/Footer/ParticipantList.tsx @@ -9,24 +9,30 @@ import { selectIsPeerAudioEnabled, selectLocalPeerID, selectPeerCount, - selectPeerMetadata, selectPermissions, - useHMSActions, useHMSStore, } from '@100mslive/react-sdk'; -import { ChangeRoleIcon, HandIcon, MicOffIcon, PeopleIcon, SearchIcon, VerticalMenuIcon } from '@100mslive/react-icons'; +import { + AddIcon, + ChangeRoleIcon, + CrossIcon, + HandIcon, + MicOffIcon, + PeopleIcon, + SearchIcon, + VerticalMenuIcon, +} from '@100mslive/react-icons'; import { Accordion, Box, config as cssConfig, Dropdown, Flex, Input, Text, textEllipsis } from '../../..'; +import { IconButton as BaseIconButton } from '../../../IconButton'; // @ts-ignore: No implicit Any import IconButton from '../../IconButton'; import { ConnectionIndicator } from '../Connection/ConnectionIndicator'; import { RemoveParticipant } from '../RemoveParticipant'; import { RoleAccordion } from './RoleAccordion'; -import { - ConferencingScreenElements, - useRoomLayoutConferencingScreen, -} from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen'; +import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen'; // @ts-ignore: No implicit Any import { useIsSidepaneTypeOpen, useSidepaneToggle } from '../AppData/useSidepane'; +import { usePeerOnStageActions } from '../hooks/usePeerOnStageActions'; import { useParticipants } from '../../common/hooks'; // @ts-ignore: No implicit Any import { getFormattedCount } from '../../common/utils'; @@ -146,10 +152,12 @@ export const ParticipantCount = () => { export const Participant = ({ peer, isConnected, + isHandRaisedAccordion, style, }: { peer: HMSPeer; isConnected: boolean; + isHandRaisedAccordion?: boolean; style: React.CSSProperties; }) => { const localPeerId = useHMSStore(selectLocalPeerID); @@ -175,7 +183,12 @@ export const Participant = ({ {peer.name} {localPeerId === peer.id ? '(You)' : ''} {isConnected && peer.roleName ? ( - + ) : null} ); @@ -247,7 +260,17 @@ const VirtualizedParticipants = ({ * shows settings to change for a participant like changing their role */ const ParticipantActions = React.memo( - ({ peerId, role, isLocal }: { peerId: string; role: string; isLocal: boolean }) => { + ({ + peerId, + role, + isLocal, + isHandRaisedAccordion, + }: { + peerId: string; + role: string; + isLocal: boolean; + isHandRaisedAccordion?: boolean; + }) => { const isHandRaised = useHMSStore(selectHasPeerHandRaised(peerId)); const canChangeRole = useHMSStore(selectPermissions)?.changeRole; const canRemoveOthers = useHMSStore(selectPermissions)?.removeOthers; @@ -264,72 +287,73 @@ const ParticipantActions = React.memo( gap: '$8', }} > - - {isHandRaised && ( - - - - )} - {isAudioMuted ? ( - - - - ) : null} + {isHandRaisedAccordion ? ( + + ) : ( + <> + + {isHandRaised && ( + + + + )} + {isAudioMuted ? ( + + + + ) : null} - {shouldShowMoreActions && !isLocal ? ( - - ) : null} + {shouldShowMoreActions && !isLocal ? : null} + + )} ); }, ); -const ParticipantMoreActions = ({ - peerId, - role, - elements, - canChangeRole, -}: { - peerId: string; - role: string; - canChangeRole: boolean; - elements: ConferencingScreenElements; -}) => { - const hmsActions = useHMSActions(); +const HandRaisedAccordionParticipantActions = ({ peerId, role }: { peerId: string; role: string }) => { + const { handleStageAction, lowerPeerHand, shouldShowStageRoleChange, isInStage } = usePeerOnStageActions({ + peerId, + role, + }); + return ( + <> + lowerPeerHand()} + > + + + {shouldShowStageRoleChange && !isInStage && ( + handleStageAction()} + > + + + )} + + ); +}; + +const ParticipantMoreActions = ({ peerId, role }: { peerId: string; role: string }) => { const { + open, + setOpen, bring_to_stage_label, remove_from_stage_label, - on_stage_role, - off_stage_roles = [], - skip_preview_for_role_change = false, - } = elements.on_stage_exp || {}; - const isInStage = role === on_stage_role; - const shouldShowStageRoleChange = - canChangeRole && - ((isInStage && remove_from_stage_label) || (off_stage_roles?.includes(role) && bring_to_stage_label)); - const prevRole = useHMSStore(selectPeerMetadata(peerId))?.prevRole; - const [open, setOpen] = useState(false); - - const handleStageAction = async () => { - if (isInStage) { - prevRole && hmsActions.changeRoleOfPeer(peerId, prevRole, true); - } else if (on_stage_role) { - await hmsActions.changeRoleOfPeer(peerId, on_stage_role, skip_preview_for_role_change); - if (skip_preview_for_role_change) { - await hmsActions.lowerRemotePeerHand(peerId); - } - } - setOpen(false); - }; - + handleStageAction, + isInStage, + shouldShowStageRoleChange, + } = usePeerOnStageActions({ peerId, role }); return ( setOpen(value)} modal={false}> @@ -63,6 +68,9 @@ export const RoleAccordion = ({ peersInAccordion = peersInAccordion.filter(peer => peer.name.toLowerCase().includes(filter.search || '')); } } + const { bringAllToStage, bring_to_stage_label, canBringToStage, lowerAllHands } = useGroupOnStageActions({ + peers: peersInAccordion, + }); useEffect(() => { if (!isOffStageRole || !isLargeRoom) { @@ -113,7 +121,7 @@ export const RoleAccordion = ({ ) : null} + {isHandRaisedAccordion && ( + <> + + + + {canBringToStage && } + + + )} ); diff --git a/packages/roomkit-react/src/Prebuilt/components/MwebLandscapePrompt.tsx b/packages/roomkit-react/src/Prebuilt/components/MwebLandscapePrompt.tsx index b3ce28e14a..c2164f9895 100644 --- a/packages/roomkit-react/src/Prebuilt/components/MwebLandscapePrompt.tsx +++ b/packages/roomkit-react/src/Prebuilt/components/MwebLandscapePrompt.tsx @@ -32,7 +32,7 @@ export const MwebLandscapePrompt = () => { // Angle check needed to diff bw mobile and desktop setShowMwebLandscapePrompt( match({ angle, isLandscapeHLSStream, isLandscape, type }) - .with({ isLandscapeHLSStream }, () => false) + .with({ isLandscapeHLSStream: true }, () => false) .with({ angle: P.when(angle => angle && angle >= 90) }, ({ type }) => type.includes('landscape')) .otherwise(() => isLandscape), ); diff --git a/packages/roomkit-react/src/Prebuilt/components/VideoLayouts/ProminenceLayout.tsx b/packages/roomkit-react/src/Prebuilt/components/VideoLayouts/ProminenceLayout.tsx index 9041d8f945..9ed895d2d8 100644 --- a/packages/roomkit-react/src/Prebuilt/components/VideoLayouts/ProminenceLayout.tsx +++ b/packages/roomkit-react/src/Prebuilt/components/VideoLayouts/ProminenceLayout.tsx @@ -36,7 +36,6 @@ const SecondarySection = ({ hasSidebar, }: React.PropsWithChildren<{ tiles: TrackWithPeerAndDimensions[]; edgeToEdge?: boolean; hasSidebar?: boolean }>) => { const tileLayoutProps = useVideoTileContext(); - console.log('secondary section', { tilesLength: tiles?.length }); if (!tiles?.length) { return null; } diff --git a/packages/roomkit-react/src/Prebuilt/components/hooks/useGroupOnStageActions.tsx b/packages/roomkit-react/src/Prebuilt/components/hooks/useGroupOnStageActions.tsx new file mode 100644 index 0000000000..fe5fafacd9 --- /dev/null +++ b/packages/roomkit-react/src/Prebuilt/components/hooks/useGroupOnStageActions.tsx @@ -0,0 +1,54 @@ +import { match, P } from 'ts-pattern'; +import { HMSPeer, selectPermissions, useHMSActions, useHMSStore } from '@100mslive/react-sdk'; +import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen'; + +export const useGroupOnStageActions = ({ peers }: { peers: HMSPeer[] }) => { + const hmsActions = useHMSActions(); + const { elements } = useRoomLayoutConferencingScreen(); + const { + bring_to_stage_label, + remove_from_stage_label, + on_stage_role, + off_stage_roles = [], + skip_preview_for_role_change = false, + } = elements.on_stage_exp || {}; + const canChangeRole = useHMSStore(selectPermissions)?.changeRole; + + const offStageRolePeers = peers.filter(peer => + match({ on_stage_role, bring_to_stage_label, roleName: peer.roleName }) + .with( + { + on_stage_role: P.when(role => !!role), + bring_to_stage_label: P.when(label => !!label), + roleName: P.when(role => !!role && off_stage_roles.includes(role)), + }, + () => true, + ) + .otherwise(() => false), + ); + + const lowerAllHands = async () => { + return Promise.all(peers.map(peer => hmsActions.lowerRemotePeerHand(peer.id))); + }; + + const bringAllToStage = () => { + if (!canChangeRole || !on_stage_role) { + return; + } + return Promise.all( + offStageRolePeers.map(peer => { + return hmsActions.changeRoleOfPeer(peer.id, on_stage_role, skip_preview_for_role_change).then(() => { + return skip_preview_for_role_change ? hmsActions.lowerRemotePeerHand(peer.id) : null; + }); + }), + ); + }; + + return { + lowerAllHands, + bringAllToStage, + canBringToStage: canChangeRole && offStageRolePeers.length > 0, + bring_to_stage_label, + remove_from_stage_label, + }; +}; diff --git a/packages/roomkit-react/src/Prebuilt/components/hooks/usePeerOnStageActions.tsx b/packages/roomkit-react/src/Prebuilt/components/hooks/usePeerOnStageActions.tsx new file mode 100644 index 0000000000..000a20641f --- /dev/null +++ b/packages/roomkit-react/src/Prebuilt/components/hooks/usePeerOnStageActions.tsx @@ -0,0 +1,49 @@ +import { useState } from 'react'; +import { selectPeerMetadata, selectPermissions, useHMSActions, useHMSStore } from '@100mslive/react-sdk'; +import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen'; + +export const usePeerOnStageActions = ({ peerId, role }: { peerId: string; role: string }) => { + const hmsActions = useHMSActions(); + const { elements } = useRoomLayoutConferencingScreen(); + const { + bring_to_stage_label, + remove_from_stage_label, + on_stage_role, + off_stage_roles = [], + skip_preview_for_role_change = false, + } = elements.on_stage_exp || {}; + const isInStage = role === on_stage_role; + const canChangeRole = useHMSStore(selectPermissions)?.changeRole; + const shouldShowStageRoleChange = + canChangeRole && + ((isInStage && remove_from_stage_label) || (off_stage_roles?.includes(role) && bring_to_stage_label)); + const prevRole = useHMSStore(selectPeerMetadata(peerId))?.prevRole; + const [open, setOpen] = useState(false); + + const lowerPeerHand = async () => { + await hmsActions.lowerRemotePeerHand(peerId); + }; + + const handleStageAction = async () => { + if (isInStage) { + prevRole && hmsActions.changeRoleOfPeer(peerId, prevRole, true); + } else if (on_stage_role) { + await hmsActions.changeRoleOfPeer(peerId, on_stage_role, skip_preview_for_role_change); + if (skip_preview_for_role_change) { + await lowerPeerHand(); + } + } + setOpen(false); + }; + + return { + open, + setOpen, + lowerPeerHand, + handleStageAction, + shouldShowStageRoleChange, + isInStage, + bring_to_stage_label, + remove_from_stage_label, + }; +}; diff --git a/packages/roomkit-react/src/Prebuilt/layouts/SidePane.tsx b/packages/roomkit-react/src/Prebuilt/layouts/SidePane.tsx index 5b4066b683..3225467f98 100644 --- a/packages/roomkit-react/src/Prebuilt/layouts/SidePane.tsx +++ b/packages/roomkit-react/src/Prebuilt/layouts/SidePane.tsx @@ -193,7 +193,6 @@ const SidePane = ({ }); if (!trackId && !SidepaneComponent) { - console.log('sidepane not rendered', { trackId, SidepaneComponent }); return null; }