@100mslive/roomkit-react 0.3.10-alpha.0 → 0.3.10-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{HLSView-5GXP76IN.js → HLSView-524T6OTY.js} +2 -2
- package/dist/{HLSView-FBGVUTA5.css → HLSView-VL3DXGRO.css} +3 -3
- package/dist/{HLSView-FBGVUTA5.css.map → HLSView-VL3DXGRO.css.map} +1 -1
- package/dist/Prebuilt/common/constants.d.ts +1 -0
- package/dist/Prebuilt/components/AppData/useSidepaneResetOnLayoutUpdate.d.ts +3 -0
- package/dist/Prebuilt/components/Chat/Chat.d.ts +1 -1
- package/dist/Prebuilt/components/RoleChangeModal.d.ts +5 -0
- package/dist/Prebuilt/components/TileMenu/TileMenuContent.d.ts +2 -1
- package/dist/{chunk-WSDBUVSZ.js → chunk-IOHV3H2B.js} +602 -561
- package/dist/chunk-IOHV3H2B.js.map +7 -0
- package/dist/index.cjs.css +2 -2
- package/dist/index.cjs.css.map +1 -1
- package/dist/index.cjs.js +1943 -1892
- package/dist/index.cjs.js.map +4 -4
- package/dist/index.css +2 -2
- package/dist/index.css.map +1 -1
- package/dist/index.js +1 -1
- package/dist/meta.cjs.json +130 -56
- package/dist/meta.esbuild.json +141 -67
- package/package.json +7 -7
- package/src/Modal/DialogContent.tsx +1 -1
- package/src/Prebuilt/common/constants.ts +2 -0
- package/src/Prebuilt/components/AppData/useSidepaneResetOnLayoutUpdate.tsx +22 -0
- package/src/Prebuilt/components/AudioVideoToggle.tsx +1 -1
- package/src/Prebuilt/components/Chat/Chat.tsx +3 -4
- package/src/Prebuilt/components/Footer/ParticipantList.tsx +56 -37
- package/src/Prebuilt/components/RoleChangeModal.tsx +187 -0
- package/src/Prebuilt/components/TileMenu/TileMenu.tsx +5 -0
- package/src/Prebuilt/components/TileMenu/TileMenuContent.tsx +23 -1
- package/src/Prebuilt/components/VirtualBackground/VBPicker.tsx +3 -0
- package/src/Prebuilt/layouts/SidePane.tsx +9 -9
- package/dist/chunk-WSDBUVSZ.js.map +0 -7
- package/src/Prebuilt/components/RoleChangeModal.jsx +0 -185
- /package/dist/{HLSView-5GXP76IN.js.map → HLSView-524T6OTY.js.map} +0 -0
| @@ -21,6 +21,7 @@ import { | |
| 21 21 | 
             
              HandIcon,
         | 
| 22 22 | 
             
              MicOffIcon,
         | 
| 23 23 | 
             
              PeopleIcon,
         | 
| 24 | 
            +
              PersonSettingsIcon,
         | 
| 24 25 | 
             
              SearchIcon,
         | 
| 25 26 | 
             
              VerticalMenuIcon,
         | 
| 26 27 | 
             
            } from '@100mslive/react-icons';
         | 
| @@ -29,10 +30,12 @@ import { Accordion, Box, Button, config as cssConfig, Dropdown, Flex, Input, Tex | |
| 29 30 | 
             
            import IconButton from '../../IconButton';
         | 
| 30 31 | 
             
            import { ConnectionIndicator } from '../Connection/ConnectionIndicator';
         | 
| 31 32 | 
             
            import { RemoveParticipant } from '../RemoveParticipant';
         | 
| 33 | 
            +
            import { RoleChangeModal } from '../RoleChangeModal';
         | 
| 32 34 | 
             
            import { RoleAccordion } from './RoleAccordion';
         | 
| 33 35 | 
             
            import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
         | 
| 34 36 | 
             
            // @ts-ignore: No implicit Any
         | 
| 35 37 | 
             
            import { useIsSidepaneTypeOpen, useSidepaneToggle } from '../AppData/useSidepane';
         | 
| 38 | 
            +
            import { useSidepaneResetOnLayoutUpdate } from '../AppData/useSidepaneResetOnLayoutUpdate';
         | 
| 36 39 | 
             
            import { usePeerOnStageActions } from '../hooks/usePeerOnStageActions';
         | 
| 37 40 | 
             
            import { useParticipants } from '../../common/hooks';
         | 
| 38 41 | 
             
            // @ts-ignore: No implicit Any
         | 
| @@ -71,6 +74,8 @@ export const ParticipantList = ({ | |
| 71 74 | 
             
                });
         | 
| 72 75 | 
             
              }
         | 
| 73 76 |  | 
| 77 | 
            +
              useSidepaneResetOnLayoutUpdate('participant_list', SIDE_PANE_OPTIONS.PARTICIPANTS);
         | 
| 78 | 
            +
             | 
| 74 79 | 
             
              const onSearch = useCallback((value: string) => {
         | 
| 75 80 | 
             
                setFilter(filterValue => {
         | 
| 76 81 | 
             
                  if (!filterValue) {
         | 
| @@ -365,45 +370,59 @@ const ParticipantMoreActions = ({ peerId, role }: { peerId: string; role: string | |
| 365 370 | 
             
                isInStage,
         | 
| 366 371 | 
             
                shouldShowStageRoleChange,
         | 
| 367 372 | 
             
              } = usePeerOnStageActions({ peerId, role });
         | 
| 373 | 
            +
              const canChangeRole = !!useHMSStore(selectPermissions)?.changeRole;
         | 
| 374 | 
            +
              const [openRoleChangeModal, setOpenRoleChangeModal] = useState(false);
         | 
| 375 | 
            +
             | 
| 368 376 | 
             
              return (
         | 
| 369 | 
            -
                 | 
| 370 | 
            -
                  <Dropdown. | 
| 371 | 
            -
                     | 
| 372 | 
            -
             | 
| 373 | 
            -
             | 
| 374 | 
            -
             | 
| 375 | 
            -
                       | 
| 376 | 
            -
             | 
| 377 | 
            -
             | 
| 378 | 
            -
             | 
| 379 | 
            -
             | 
| 380 | 
            -
                         | 
| 381 | 
            -
             | 
| 382 | 
            -
             | 
| 383 | 
            -
                         | 
| 384 | 
            -
             | 
| 385 | 
            -
             | 
| 386 | 
            -
             | 
| 387 | 
            -
             | 
| 388 | 
            -
                     | 
| 389 | 
            -
                      < | 
| 390 | 
            -
             | 
| 391 | 
            -
             | 
| 392 | 
            -
             | 
| 393 | 
            -
                    <Dropdown. | 
| 394 | 
            -
                      { | 
| 395 | 
            -
                         | 
| 396 | 
            -
                          < | 
| 397 | 
            -
             | 
| 398 | 
            -
                            { | 
| 399 | 
            -
             | 
| 400 | 
            -
             | 
| 401 | 
            -
             | 
| 377 | 
            +
                <>
         | 
| 378 | 
            +
                  <Dropdown.Root open={open} onOpenChange={value => setOpen(value)} modal={false}>
         | 
| 379 | 
            +
                    <Dropdown.Trigger
         | 
| 380 | 
            +
                      asChild
         | 
| 381 | 
            +
                      data-testid="participant_more_actions"
         | 
| 382 | 
            +
                      className="participant_item"
         | 
| 383 | 
            +
                      css={{
         | 
| 384 | 
            +
                        p: '$1',
         | 
| 385 | 
            +
                        r: '$0',
         | 
| 386 | 
            +
                        c: '$on_surface_high',
         | 
| 387 | 
            +
                        display: open ? 'flex' : 'none',
         | 
| 388 | 
            +
                        '&:hover': {
         | 
| 389 | 
            +
                          bg: '$surface_bright',
         | 
| 390 | 
            +
                        },
         | 
| 391 | 
            +
                        '@md': {
         | 
| 392 | 
            +
                          display: 'flex',
         | 
| 393 | 
            +
                        },
         | 
| 394 | 
            +
                      }}
         | 
| 395 | 
            +
                      tabIndex={0}
         | 
| 396 | 
            +
                    >
         | 
| 397 | 
            +
                      <Box css={{ my: 'auto' }}>
         | 
| 398 | 
            +
                        <VerticalMenuIcon />
         | 
| 399 | 
            +
                      </Box>
         | 
| 400 | 
            +
                    </Dropdown.Trigger>
         | 
| 401 | 
            +
                    <Dropdown.Portal>
         | 
| 402 | 
            +
                      <Dropdown.Content align="end" sideOffset={8} css={{ w: '$64', bg: '$surface_default' }}>
         | 
| 403 | 
            +
                        {shouldShowStageRoleChange ? (
         | 
| 404 | 
            +
                          <Dropdown.Item css={{ bg: '$surface_default' }} onClick={() => handleStageAction()}>
         | 
| 405 | 
            +
                            <ChangeRoleIcon />
         | 
| 406 | 
            +
                            <Text variant="sm" css={{ ml: '$4', fontWeight: '$semiBold', c: '$on_surface_high' }}>
         | 
| 407 | 
            +
                              {isInStage ? remove_from_stage_label : bring_to_stage_label}
         | 
| 408 | 
            +
                            </Text>
         | 
| 409 | 
            +
                          </Dropdown.Item>
         | 
| 410 | 
            +
                        ) : null}
         | 
| 402 411 |  | 
| 403 | 
            -
             | 
| 404 | 
            -
             | 
| 405 | 
            -
             | 
| 406 | 
            -
             | 
| 412 | 
            +
                        {canChangeRole ? (
         | 
| 413 | 
            +
                          <Dropdown.Item css={{ bg: '$surface_default' }} onClick={() => setOpenRoleChangeModal(true)}>
         | 
| 414 | 
            +
                            <PersonSettingsIcon />
         | 
| 415 | 
            +
                            <Text variant="sm" css={{ ml: '$4', fontWeight: '$semiBold', c: '$on_surface_high' }}>
         | 
| 416 | 
            +
                              Switch Role
         | 
| 417 | 
            +
                            </Text>
         | 
| 418 | 
            +
                          </Dropdown.Item>
         | 
| 419 | 
            +
                        ) : null}
         | 
| 420 | 
            +
                        <RemoveParticipant peerId={peerId} />
         | 
| 421 | 
            +
                      </Dropdown.Content>
         | 
| 422 | 
            +
                    </Dropdown.Portal>
         | 
| 423 | 
            +
                  </Dropdown.Root>
         | 
| 424 | 
            +
                  {openRoleChangeModal && <RoleChangeModal peerId={peerId} onOpenChange={setOpenRoleChangeModal} />}
         | 
| 425 | 
            +
                </>
         | 
| 407 426 | 
             
              );
         | 
| 408 427 | 
             
            };
         | 
| 409 428 |  | 
| @@ -0,0 +1,187 @@ | |
| 1 | 
            +
            import React, { useRef, useState } from 'react';
         | 
| 2 | 
            +
            import { useMedia } from 'react-use';
         | 
| 3 | 
            +
            import { HMSPeer, selectAvailableRoleNames, selectPeerByID, useHMSActions, useHMSStore } from '@100mslive/react-sdk';
         | 
| 4 | 
            +
            import { ChevronDownIcon, ChevronUpIcon, CrossIcon } from '@100mslive/react-icons';
         | 
| 5 | 
            +
            import { Button } from '../../Button';
         | 
| 6 | 
            +
            import { Dropdown } from '../../Dropdown';
         | 
| 7 | 
            +
            import { Box, Flex } from '../../Layout';
         | 
| 8 | 
            +
            import { Dialog } from '../../Modal';
         | 
| 9 | 
            +
            import { Sheet } from '../../Sheet';
         | 
| 10 | 
            +
            import { Text } from '../../Text';
         | 
| 11 | 
            +
            import { config as cssConfig } from '../../Theme';
         | 
| 12 | 
            +
            import { Tooltip } from '../../Tooltip';
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            const HighlightTerm = ({ value }: { value: string | undefined }) => {
         | 
| 15 | 
            +
              return value ? (
         | 
| 16 | 
            +
                <Tooltip side="top" title={value}>
         | 
| 17 | 
            +
                  <Text
         | 
| 18 | 
            +
                    variant="body2"
         | 
| 19 | 
            +
                    css={{
         | 
| 20 | 
            +
                      color: '$on_surface_medium',
         | 
| 21 | 
            +
                      fontWeight: '$semiBold',
         | 
| 22 | 
            +
                    }}
         | 
| 23 | 
            +
                  >
         | 
| 24 | 
            +
                    '{value.slice(0, 100)}
         | 
| 25 | 
            +
                    {value.length > 100 ? '...' : ''}'
         | 
| 26 | 
            +
                  </Text>
         | 
| 27 | 
            +
                </Tooltip>
         | 
| 28 | 
            +
              ) : (
         | 
| 29 | 
            +
                <></>
         | 
| 30 | 
            +
              );
         | 
| 31 | 
            +
            };
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            const RoleChangeContent = ({
         | 
| 34 | 
            +
              peer,
         | 
| 35 | 
            +
              onOpenChange,
         | 
| 36 | 
            +
              isMobile = false,
         | 
| 37 | 
            +
            }: {
         | 
| 38 | 
            +
              peer: HMSPeer;
         | 
| 39 | 
            +
              onOpenChange: (open: boolean) => void;
         | 
| 40 | 
            +
              isMobile?: boolean;
         | 
| 41 | 
            +
            }) => {
         | 
| 42 | 
            +
              const roles = useHMSStore(selectAvailableRoleNames).filter(role => role !== peer?.roleName);
         | 
| 43 | 
            +
              const [selectedRole, setRole] = useState(roles.filter(role => role !== peer?.roleName)?.[0] || peer?.roleName);
         | 
| 44 | 
            +
              const hmsActions = useHMSActions();
         | 
| 45 | 
            +
              const [open, setOpen] = useState(false);
         | 
| 46 | 
            +
              const triggerRef = useRef<HTMLButtonElement | undefined>();
         | 
| 47 | 
            +
             | 
| 48 | 
            +
              return (
         | 
| 49 | 
            +
                <>
         | 
| 50 | 
            +
                  <Flex align="center" justify="between" css={{ w: '100%' }}>
         | 
| 51 | 
            +
                    <Text as="h6" variant="h6">
         | 
| 52 | 
            +
                      Switch Role
         | 
| 53 | 
            +
                    </Text>
         | 
| 54 | 
            +
                    {isMobile && <CrossIcon onClick={() => onOpenChange(false)} />}
         | 
| 55 | 
            +
                  </Flex>
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  <Box>
         | 
| 58 | 
            +
                    <Text
         | 
| 59 | 
            +
                      variant="body2"
         | 
| 60 | 
            +
                      css={{
         | 
| 61 | 
            +
                        mt: '$4',
         | 
| 62 | 
            +
                        mb: '$8',
         | 
| 63 | 
            +
                        c: '$on_surface_medium',
         | 
| 64 | 
            +
                        display: 'flex',
         | 
| 65 | 
            +
                        flexWrap: 'wrap',
         | 
| 66 | 
            +
                        columnGap: '4px',
         | 
| 67 | 
            +
                      }}
         | 
| 68 | 
            +
                    >
         | 
| 69 | 
            +
                      Switch the role of
         | 
| 70 | 
            +
                      <HighlightTerm value={peer.name} />
         | 
| 71 | 
            +
                      from <HighlightTerm value={peer.roleName} />
         | 
| 72 | 
            +
                    </Text>
         | 
| 73 | 
            +
                  </Box>
         | 
| 74 | 
            +
                  <Flex
         | 
| 75 | 
            +
                    align="center"
         | 
| 76 | 
            +
                    css={{
         | 
| 77 | 
            +
                      w: '100%',
         | 
| 78 | 
            +
                      mb: '$10',
         | 
| 79 | 
            +
                    }}
         | 
| 80 | 
            +
                  >
         | 
| 81 | 
            +
                    <Box
         | 
| 82 | 
            +
                      css={{
         | 
| 83 | 
            +
                        position: 'relative',
         | 
| 84 | 
            +
                        flex: '1 1 0',
         | 
| 85 | 
            +
                        minWidth: 0,
         | 
| 86 | 
            +
                      }}
         | 
| 87 | 
            +
                    >
         | 
| 88 | 
            +
                      <Dropdown.Root open={open} onOpenChange={setOpen} css={{ width: '100%' }}>
         | 
| 89 | 
            +
                        <Dropdown.Trigger
         | 
| 90 | 
            +
                          // @ts-ignore
         | 
| 91 | 
            +
                          ref={triggerRef}
         | 
| 92 | 
            +
                          data-testid="open_role_selection_dropdown"
         | 
| 93 | 
            +
                          asChild
         | 
| 94 | 
            +
                          css={{
         | 
| 95 | 
            +
                            border: '1px solid $border_bright',
         | 
| 96 | 
            +
                            bg: '$surface_default',
         | 
| 97 | 
            +
                            r: '$1',
         | 
| 98 | 
            +
                            p: '$6 $9',
         | 
| 99 | 
            +
                          }}
         | 
| 100 | 
            +
                        >
         | 
| 101 | 
            +
                          <Flex align="center" justify="between" css={{ width: '100%' }}>
         | 
| 102 | 
            +
                            <Text>{selectedRole}</Text>
         | 
| 103 | 
            +
                            {open ? <ChevronUpIcon /> : <ChevronDownIcon />}
         | 
| 104 | 
            +
                          </Flex>
         | 
| 105 | 
            +
                        </Dropdown.Trigger>
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                        <Dropdown.Content align="start" sideOffset={8} css={{ zIndex: 1000, w: '100%' }}>
         | 
| 108 | 
            +
                          {roles.map(role => (
         | 
| 109 | 
            +
                            <Dropdown.Item
         | 
| 110 | 
            +
                              data-testid={role}
         | 
| 111 | 
            +
                              key={role}
         | 
| 112 | 
            +
                              onSelect={() => setRole(role)}
         | 
| 113 | 
            +
                              css={{ w: `${triggerRef.current?.clientWidth}px` }}
         | 
| 114 | 
            +
                            >
         | 
| 115 | 
            +
                              {role}
         | 
| 116 | 
            +
                            </Dropdown.Item>
         | 
| 117 | 
            +
                          ))}
         | 
| 118 | 
            +
                        </Dropdown.Content>
         | 
| 119 | 
            +
                      </Dropdown.Root>
         | 
| 120 | 
            +
                    </Box>
         | 
| 121 | 
            +
                  </Flex>
         | 
| 122 | 
            +
                  <Flex justify="center" align="center" css={{ width: '100%', gap: '$md' }}>
         | 
| 123 | 
            +
                    {!isMobile && (
         | 
| 124 | 
            +
                      <Button
         | 
| 125 | 
            +
                        variant="standard"
         | 
| 126 | 
            +
                        outlined
         | 
| 127 | 
            +
                        css={{ width: '100%' }}
         | 
| 128 | 
            +
                        onClick={() => onOpenChange(false)}
         | 
| 129 | 
            +
                        data-testid="cancel_button"
         | 
| 130 | 
            +
                      >
         | 
| 131 | 
            +
                        Cancel
         | 
| 132 | 
            +
                      </Button>
         | 
| 133 | 
            +
                    )}
         | 
| 134 | 
            +
             | 
| 135 | 
            +
                    <Button
         | 
| 136 | 
            +
                      data-testid="change_button"
         | 
| 137 | 
            +
                      variant="primary"
         | 
| 138 | 
            +
                      css={{ width: '100%' }}
         | 
| 139 | 
            +
                      onClick={async () => {
         | 
| 140 | 
            +
                        if (selectedRole) {
         | 
| 141 | 
            +
                          await hmsActions.changeRoleOfPeer(peer.id, selectedRole, true);
         | 
| 142 | 
            +
                          onOpenChange(false);
         | 
| 143 | 
            +
                        }
         | 
| 144 | 
            +
                      }}
         | 
| 145 | 
            +
                    >
         | 
| 146 | 
            +
                      Switch Role
         | 
| 147 | 
            +
                    </Button>
         | 
| 148 | 
            +
                  </Flex>
         | 
| 149 | 
            +
                </>
         | 
| 150 | 
            +
              );
         | 
| 151 | 
            +
            };
         | 
| 152 | 
            +
             | 
| 153 | 
            +
            export const RoleChangeModal = ({
         | 
| 154 | 
            +
              peerId,
         | 
| 155 | 
            +
              onOpenChange,
         | 
| 156 | 
            +
            }: {
         | 
| 157 | 
            +
              peerId: string;
         | 
| 158 | 
            +
              onOpenChange: (open: boolean) => void;
         | 
| 159 | 
            +
            }) => {
         | 
| 160 | 
            +
              const peer = useHMSStore(selectPeerByID(peerId));
         | 
| 161 | 
            +
              const isMobile = useMedia(cssConfig.media.md);
         | 
| 162 | 
            +
             | 
| 163 | 
            +
              if (!peer) {
         | 
| 164 | 
            +
                return null;
         | 
| 165 | 
            +
              }
         | 
| 166 | 
            +
             | 
| 167 | 
            +
              if (isMobile) {
         | 
| 168 | 
            +
                return (
         | 
| 169 | 
            +
                  <Sheet.Root open={true} onOpenChange={onOpenChange}>
         | 
| 170 | 
            +
                    <Sheet.Content css={{ p: '$12 $8', background: '$surface_dim' }}>
         | 
| 171 | 
            +
                      <RoleChangeContent peer={peer} onOpenChange={onOpenChange} isMobile />
         | 
| 172 | 
            +
                    </Sheet.Content>
         | 
| 173 | 
            +
                  </Sheet.Root>
         | 
| 174 | 
            +
                );
         | 
| 175 | 
            +
              }
         | 
| 176 | 
            +
             | 
| 177 | 
            +
              return (
         | 
| 178 | 
            +
                <Dialog.Root defaultOpen onOpenChange={onOpenChange}>
         | 
| 179 | 
            +
                  <Dialog.Portal>
         | 
| 180 | 
            +
                    <Dialog.Overlay />
         | 
| 181 | 
            +
                    <Dialog.Content css={{ width: 'min(400px,80%)', p: '$10' }}>
         | 
| 182 | 
            +
                      <RoleChangeContent peer={peer} onOpenChange={onOpenChange} />
         | 
| 183 | 
            +
                    </Dialog.Content>
         | 
| 184 | 
            +
                  </Dialog.Portal>
         | 
| 185 | 
            +
                </Dialog.Root>
         | 
| 186 | 
            +
              );
         | 
| 187 | 
            +
            };
         | 
| @@ -17,6 +17,7 @@ import { Text } from '../../../Text'; | |
| 17 17 | 
             
            import { config as cssConfig, useTheme } from '../../../Theme';
         | 
| 18 18 | 
             
            import { StyledMenuTile } from '../../../TileMenu';
         | 
| 19 19 | 
             
            import { ChangeNameModal } from '../MoreSettings/ChangeNameModal';
         | 
| 20 | 
            +
            import { RoleChangeModal } from '../RoleChangeModal';
         | 
| 20 21 | 
             
            import { TileMenuContent } from './TileMenuContent';
         | 
| 21 22 | 
             
            import { useDropdownList } from '../hooks/useDropdownList';
         | 
| 22 23 | 
             
            import { getDragClassName } from './utils';
         | 
| @@ -56,6 +57,7 @@ const TileMenu = ({ | |
| 56 57 | 
             
              const isMobile = useMedia(cssConfig.media.md);
         | 
| 57 58 | 
             
              const peer = useHMSStore(selectPeerByID(peerID));
         | 
| 58 59 | 
             
              const [showNameChangeModal, setShowNameChangeModal] = useState(false);
         | 
| 60 | 
            +
              const [showRoleChangeModal, setShowRoleChangeModal] = useState(false);
         | 
| 59 61 | 
             
              useDropdownList({ open, name: 'TileMenu' });
         | 
| 60 62 | 
             
              const dragClassName = getDragClassName();
         | 
| 61 63 |  | 
| @@ -64,6 +66,7 @@ const TileMenu = ({ | |
| 64 66 | 
             
              }
         | 
| 65 67 |  | 
| 66 68 | 
             
              const openNameChangeModal = () => setShowNameChangeModal(true);
         | 
| 69 | 
            +
              const openRoleChangeModal = () => setShowRoleChangeModal(true);
         | 
| 67 70 |  | 
| 68 71 | 
             
              const props = {
         | 
| 69 72 | 
             
                isLocal,
         | 
| @@ -76,6 +79,7 @@ const TileMenu = ({ | |
| 76 79 | 
             
                showPinAction,
         | 
| 77 80 | 
             
                canMinimise,
         | 
| 78 81 | 
             
                openNameChangeModal,
         | 
| 82 | 
            +
                openRoleChangeModal,
         | 
| 79 83 | 
             
              };
         | 
| 80 84 |  | 
| 81 85 | 
             
              return (
         | 
| @@ -133,6 +137,7 @@ const TileMenu = ({ | |
| 133 137 | 
             
                    )}
         | 
| 134 138 | 
             
                  </StyledMenuTile.Root>
         | 
| 135 139 | 
             
                  {showNameChangeModal && <ChangeNameModal onOpenChange={setShowNameChangeModal} />}
         | 
| 140 | 
            +
                  {showRoleChangeModal && <RoleChangeModal peerId={peerID} onOpenChange={setShowRoleChangeModal} />}
         | 
| 136 141 | 
             
                </>
         | 
| 137 142 | 
             
              );
         | 
| 138 143 | 
             
            };
         | 
| @@ -17,6 +17,7 @@ import { | |
| 17 17 | 
             
              MicOffIcon,
         | 
| 18 18 | 
             
              MicOnIcon,
         | 
| 19 19 | 
             
              PencilIcon,
         | 
| 20 | 
            +
              PersonSettingsIcon,
         | 
| 20 21 | 
             
              PinIcon,
         | 
| 21 22 | 
             
              RemoveUserIcon,
         | 
| 22 23 | 
             
              ShareScreenIcon,
         | 
| @@ -224,6 +225,9 @@ export const TileMenuContent = ({ | |
| 224 225 | 
             
              openNameChangeModal = () => {
         | 
| 225 226 | 
             
                return;
         | 
| 226 227 | 
             
              },
         | 
| 228 | 
            +
              openRoleChangeModal = () => {
         | 
| 229 | 
            +
                return;
         | 
| 230 | 
            +
              },
         | 
| 227 231 | 
             
            }: {
         | 
| 228 232 | 
             
              videoTrackID: string;
         | 
| 229 233 | 
             
              audioTrackID: string;
         | 
| @@ -235,10 +239,13 @@ export const TileMenuContent = ({ | |
| 235 239 | 
             
              canMinimise?: boolean;
         | 
| 236 240 | 
             
              closeSheetOnClick?: () => void;
         | 
| 237 241 | 
             
              openNameChangeModal?: () => void;
         | 
| 242 | 
            +
              openRoleChangeModal?: () => void;
         | 
| 238 243 | 
             
            }) => {
         | 
| 239 244 | 
             
              const actions = useHMSActions();
         | 
| 240 245 | 
             
              const dragClassName = getDragClassName();
         | 
| 241 | 
            -
              const  | 
| 246 | 
            +
              const permissions = useHMSStore(selectPermissions);
         | 
| 247 | 
            +
              const canChangeRole = !!permissions?.changeRole;
         | 
| 248 | 
            +
              const removeOthers = !!permissions?.removeOthers;
         | 
| 242 249 | 
             
              const { userName } = useHMSPrebuiltContext();
         | 
| 243 250 |  | 
| 244 251 | 
             
              const { isAudioEnabled, isVideoEnabled, setVolume, toggleAudio, toggleVideo, volume } = useRemoteAVToggle(
         | 
| @@ -308,6 +315,21 @@ export const TileMenuContent = ({ | |
| 308 315 | 
             
                    </StyledMenuTile.ItemButton>
         | 
| 309 316 | 
             
                  ) : null}
         | 
| 310 317 |  | 
| 318 | 
            +
                  {canChangeRole ? (
         | 
| 319 | 
            +
                    <StyledMenuTile.ItemButton
         | 
| 320 | 
            +
                      className={dragClassName}
         | 
| 321 | 
            +
                      css={spacingCSS}
         | 
| 322 | 
            +
                      onClick={() => {
         | 
| 323 | 
            +
                        openRoleChangeModal();
         | 
| 324 | 
            +
                        closeSheetOnClick();
         | 
| 325 | 
            +
                      }}
         | 
| 326 | 
            +
                      data-testid="change_role_btn"
         | 
| 327 | 
            +
                    >
         | 
| 328 | 
            +
                      <PersonSettingsIcon height={20} width={20} />
         | 
| 329 | 
            +
                      <span>Switch Role</span>
         | 
| 330 | 
            +
                    </StyledMenuTile.ItemButton>
         | 
| 331 | 
            +
                  ) : null}
         | 
| 332 | 
            +
             | 
| 311 333 | 
             
                  {audioTrackID ? (
         | 
| 312 334 | 
             
                    <StyledMenuTile.VolumeItem data-testid="participant_volume_slider" css={{ ...spacingCSS, mb: '$0' }}>
         | 
| 313 335 | 
             
                      <Flex align="center" gap={1}>
         | 
| @@ -26,6 +26,7 @@ import { VBCollection } from './VBCollection'; | |
| 26 26 | 
             
            import { VBHandler } from './VBHandler';
         | 
| 27 27 | 
             
            // @ts-ignore
         | 
| 28 28 | 
             
            import { useSidepaneToggle } from '../AppData/useSidepane';
         | 
| 29 | 
            +
            import { useSidepaneResetOnLayoutUpdate } from '../AppData/useSidepaneResetOnLayoutUpdate';
         | 
| 29 30 | 
             
            // @ts-ignore
         | 
| 30 31 | 
             
            import { useSetAppDataByKey, useUISettings } from '../AppData/useUISettings';
         | 
| 31 32 | 
             
            import { APP_DATA, SIDE_PANE_OPTIONS, UI_SETTINGS } from '../../common/constants';
         | 
| @@ -113,6 +114,8 @@ export const VBPicker = ({ backgroundMedia = [] }: { backgroundMedia: VirtualBac | |
| 113 114 | 
             
                return () => setLoadingEffects(false);
         | 
| 114 115 | 
             
              }, [isVideoOn, setLoadingEffects, toggleVB]);
         | 
| 115 116 |  | 
| 117 | 
            +
              useSidepaneResetOnLayoutUpdate('virtual_background', SIDE_PANE_OPTIONS.VB);
         | 
| 118 | 
            +
             | 
| 116 119 | 
             
              return (
         | 
| 117 120 | 
             
                <Flex css={{ pr: '$6', size: '100%' }} direction="column">
         | 
| 118 121 | 
             
                  <Flex align="center" justify="between" css={{ w: '100%', background: '$surface_dim', pb: '$4' }}>
         | 
| @@ -141,14 +141,6 @@ const SidePane = ({ | |
| 141 141 | 
             
                ? preview_elements?.virtual_background?.background_media
         | 
| 142 142 | 
             
                : elements?.virtual_background?.background_media || [];
         | 
| 143 143 |  | 
| 144 | 
            -
              const resetSidePane = useSidepaneReset();
         | 
| 145 | 
            -
             | 
| 146 | 
            -
              useEffect(() => {
         | 
| 147 | 
            -
                return () => {
         | 
| 148 | 
            -
                  resetSidePane();
         | 
| 149 | 
            -
                };
         | 
| 150 | 
            -
              }, [resetSidePane]);
         | 
| 151 | 
            -
             | 
| 152 144 | 
             
              const tileLayout = {
         | 
| 153 145 | 
             
                hideParticipantNameOnTile: tileProps?.hide_participant_name_on_tile,
         | 
| 154 146 | 
             
                roundedVideoTile: tileProps?.rounded_video_tile,
         | 
| @@ -199,7 +191,15 @@ const SidePane = ({ | |
| 199 191 | 
             
                  return null;
         | 
| 200 192 | 
             
                });
         | 
| 201 193 |  | 
| 202 | 
            -
               | 
| 194 | 
            +
              const resetSidePane = useSidepaneReset();
         | 
| 195 | 
            +
             | 
| 196 | 
            +
              useEffect(() => {
         | 
| 197 | 
            +
                return () => {
         | 
| 198 | 
            +
                  resetSidePane();
         | 
| 199 | 
            +
                };
         | 
| 200 | 
            +
              }, [resetSidePane]);
         | 
| 201 | 
            +
             | 
| 202 | 
            +
              if (!SidepaneComponent && !trackId) {
         | 
| 203 203 | 
             
                return null;
         | 
| 204 204 | 
             
              }
         | 
| 205 205 |  |