Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: lower hand raise, bring to stage in hand raised accordion #2673

Merged
merged 4 commits into from
Mar 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -146,10 +152,12 @@ export const ParticipantCount = () => {
export const Participant = ({
peer,
isConnected,
isHandRaisedAccordion,
style,
}: {
peer: HMSPeer;
isConnected: boolean;
isHandRaisedAccordion?: boolean;
raviteja83 marked this conversation as resolved.
Show resolved Hide resolved
style: React.CSSProperties;
}) => {
const localPeerId = useHMSStore(selectLocalPeerID);
Expand All @@ -175,7 +183,12 @@ export const Participant = ({
{peer.name} {localPeerId === peer.id ? '(You)' : ''}
</Text>
{isConnected && peer.roleName ? (
<ParticipantActions peerId={peer.id} isLocal={peer.id === localPeerId} role={peer.roleName} />
<ParticipantActions
peerId={peer.id}
isLocal={peer.id === localPeerId}
role={peer.roleName}
isHandRaisedAccordion={isHandRaisedAccordion}
/>
) : null}
</Flex>
);
Expand Down Expand Up @@ -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;
Expand All @@ -264,72 +287,73 @@ const ParticipantActions = React.memo(
gap: '$8',
}}
>
<ConnectionIndicator peerId={peerId} />
{isHandRaised && (
<Flex
align="center"
justify="center"
css={{ p: '$1', c: '$on_surface_high', bg: '$surface_bright', borderRadius: '$round' }}
>
<HandIcon height={19} width={19} />
</Flex>
)}
{isAudioMuted ? (
<Flex
align="center"
justify="center"
css={{ p: '$2', c: '$on_surface_high', bg: '$surface_bright', borderRadius: '$round' }}
>
<MicOffIcon height={19} width={19} />
</Flex>
) : null}
{isHandRaisedAccordion ? (
<HandRaisedAccordionParticipantActions peerId={peerId} role={role} />
) : (
<>
<ConnectionIndicator peerId={peerId} />
{isHandRaised && (
<Flex
align="center"
justify="center"
css={{ p: '$1', c: '$on_surface_high', bg: '$surface_bright', borderRadius: '$round' }}
>
<HandIcon height={19} width={19} />
</Flex>
)}
{isAudioMuted ? (
<Flex
align="center"
justify="center"
css={{ p: '$2', c: '$on_surface_high', bg: '$surface_bright', borderRadius: '$round' }}
>
<MicOffIcon height={19} width={19} />
</Flex>
) : null}

{shouldShowMoreActions && !isLocal ? (
<ParticipantMoreActions peerId={peerId} role={role} elements={elements} canChangeRole={!!canChangeRole} />
) : null}
{shouldShowMoreActions && !isLocal ? <ParticipantMoreActions peerId={peerId} role={role} /> : null}
</>
)}
</Flex>
);
},
);

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 (
<>
<BaseIconButton
css={{ p: '$1', c: '$on_surface_high', bg: '$surface_bright', borderRadius: '$round' }}
onClick={() => lowerPeerHand()}
>
<CrossIcon height={19} width={19} />
</BaseIconButton>
{shouldShowStageRoleChange && !isInStage && (
<BaseIconButton
css={{ p: '$1', c: '$on_surface_high', bg: '$primary_default', borderRadius: '$round' }}
onClick={() => handleStageAction()}
>
<AddIcon height={19} width={19} />
</BaseIconButton>
)}
</>
);
};

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 (
<Dropdown.Root open={open} onOpenChange={value => setOpen(value)} modal={false}>
<Dropdown.Trigger
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ import { FixedSizeList } from 'react-window';
import { HMSPeer, selectIsLargeRoom, useHMSStore, usePaginatedParticipants } from '@100mslive/react-sdk';
import { ChevronRightIcon } from '@100mslive/react-icons';
import { Accordion } from '../../../Accordion';
import { Button } from '../../../Button';
import { HorizontalDivider } from '../../../Divider';
import { Flex } from '../../../Layout';
import { Text } from '../../../Text';
import { Participant } from './ParticipantList';
import { RoleOptions } from './RoleOptions';
import { useGroupOnStageActions } from '../hooks/useGroupOnStageActions';
// @ts-ignore: No implicit Any
import { getFormattedCount } from '../../common/utils';

Expand All @@ -16,6 +19,7 @@ const ITER_TIMER = 5000;

export interface ItemData {
peerList: HMSPeer[];
isHandRaisedAccordion?: boolean;
isConnected: boolean;
}

Expand All @@ -29,6 +33,7 @@ export const VirtualizedParticipantItem = React.memo(
<Participant
key={data.peerList[index].id}
peer={data.peerList[index]}
isHandRaisedAccordion={data.isHandRaisedAccordion}
isConnected={data.isConnected}
style={style}
/>
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -113,7 +121,7 @@ export const RoleAccordion = ({
<Accordion.Content contentStyles={{ border: '1px solid $border_default', borderTop: 'none' }}>
<FixedSizeList
itemSize={ROW_HEIGHT}
itemData={{ peerList: peersInAccordion, isConnected }}
itemData={{ peerList: peersInAccordion, isConnected, isHandRaisedAccordion }}
itemKey={itemKey}
itemCount={peersInAccordion.length}
width={width}
Expand All @@ -140,6 +148,17 @@ export const RoleAccordion = ({
<ChevronRightIcon />
</Flex>
) : null}
{isHandRaisedAccordion && (
<>
<HorizontalDivider />
<Flex css={{ w: '100%', p: '$6', gap: '$4' }} justify="center">
<Button variant="standard" onClick={() => lowerAllHands()}>
Lower All Hands
</Button>
{canBringToStage && <Button onClick={() => bringAllToStage()}>{bring_to_stage_label}</Button>}
</Flex>
</>
)}
</Accordion.Content>
</Accordion.Item>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -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,
};
};
Original file line number Diff line number Diff line change
@@ -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));
raviteja83 marked this conversation as resolved.
Show resolved Hide resolved
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,
};
};
Loading