feat(breakout-rooms) add context menu to participants in other rooms
This commit is contained in:
parent
7dca91a50a
commit
ddce2e6bec
|
@ -142,7 +142,9 @@
|
|||
"@babel/preset-react": "7.16.0",
|
||||
"@babel/runtime": "7.16.0",
|
||||
"@jitsi/eslint-config": "4.0.0",
|
||||
"@types/react": "17.0.14",
|
||||
"@types/react-native": "0.67.6",
|
||||
"@types/react-redux": "7.1.24",
|
||||
"@types/uuid": "8.3.4",
|
||||
"babel-loader": "8.2.3",
|
||||
"babel-plugin-optional-require": "0.3.1",
|
||||
|
@ -5383,6 +5385,16 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.41.tgz",
|
||||
"integrity": "sha512-ewXv/ceBaJprikMcxCmWU1FKyMAQ2X7a9Gtmzw8fcg2kIePI1crERDM818W+XYrxqdBBOdlf2rm137bU+BltCA=="
|
||||
},
|
||||
"node_modules/@types/hoist-non-react-statics": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
|
||||
"integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/react": "*",
|
||||
"hoist-non-react-statics": "^3.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/http-proxy": {
|
||||
"version": "1.17.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.8.tgz",
|
||||
|
@ -5479,9 +5491,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "17.0.39",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.39.tgz",
|
||||
"integrity": "sha512-UVavlfAxDd/AgAacMa60Azl7ygyQNRwC/DsHZmKgNvPmRR5p70AJ5Q9EAmL2NWOJmeV+vVUI4IAP7GZrN8h8Ug==",
|
||||
"version": "17.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.14.tgz",
|
||||
"integrity": "sha512-0WwKHUbWuQWOce61UexYuWTGuGY/8JvtUe/dtQ6lR4sZ3UiylHotJeWpf3ArP9+DSGUoLY3wbU59VyMrJps5VQ==",
|
||||
"dependencies": {
|
||||
"@types/prop-types": "*",
|
||||
"@types/scheduler": "*",
|
||||
|
@ -5497,6 +5509,18 @@
|
|||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-redux": {
|
||||
"version": "7.1.24",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.24.tgz",
|
||||
"integrity": "sha512-7FkurKcS1k0FHZEtdbbgN8Oc6b+stGSfZYjQGicofJ0j4U0qIn/jaSvnP2pLwZKiai3/17xqqxkkrxTgN8UNbQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/hoist-non-react-statics": "^3.3.0",
|
||||
"@types/react": "*",
|
||||
"hoist-non-react-statics": "^3.3.0",
|
||||
"redux": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-transition-group": {
|
||||
"version": "4.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.4.tgz",
|
||||
|
@ -24022,6 +24046,16 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.41.tgz",
|
||||
"integrity": "sha512-ewXv/ceBaJprikMcxCmWU1FKyMAQ2X7a9Gtmzw8fcg2kIePI1crERDM818W+XYrxqdBBOdlf2rm137bU+BltCA=="
|
||||
},
|
||||
"@types/hoist-non-react-statics": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
|
||||
"integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/react": "*",
|
||||
"hoist-non-react-statics": "^3.3.0"
|
||||
}
|
||||
},
|
||||
"@types/http-proxy": {
|
||||
"version": "1.17.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.8.tgz",
|
||||
|
@ -24118,9 +24152,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"@types/react": {
|
||||
"version": "17.0.39",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.39.tgz",
|
||||
"integrity": "sha512-UVavlfAxDd/AgAacMa60Azl7ygyQNRwC/DsHZmKgNvPmRR5p70AJ5Q9EAmL2NWOJmeV+vVUI4IAP7GZrN8h8Ug==",
|
||||
"version": "17.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.14.tgz",
|
||||
"integrity": "sha512-0WwKHUbWuQWOce61UexYuWTGuGY/8JvtUe/dtQ6lR4sZ3UiylHotJeWpf3ArP9+DSGUoLY3wbU59VyMrJps5VQ==",
|
||||
"requires": {
|
||||
"@types/prop-types": "*",
|
||||
"@types/scheduler": "*",
|
||||
|
@ -24136,6 +24170,18 @@
|
|||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"@types/react-redux": {
|
||||
"version": "7.1.24",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.24.tgz",
|
||||
"integrity": "sha512-7FkurKcS1k0FHZEtdbbgN8Oc6b+stGSfZYjQGicofJ0j4U0qIn/jaSvnP2pLwZKiai3/17xqqxkkrxTgN8UNbQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/hoist-non-react-statics": "^3.3.0",
|
||||
"@types/react": "*",
|
||||
"hoist-non-react-statics": "^3.3.0",
|
||||
"redux": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"@types/react-transition-group": {
|
||||
"version": "4.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.4.tgz",
|
||||
|
|
|
@ -147,7 +147,9 @@
|
|||
"@babel/preset-react": "7.16.0",
|
||||
"@babel/runtime": "7.16.0",
|
||||
"@jitsi/eslint-config": "4.0.0",
|
||||
"@types/react": "17.0.14",
|
||||
"@types/react-native": "0.67.6",
|
||||
"@types/react-redux": "7.1.24",
|
||||
"@types/uuid": "8.3.4",
|
||||
"babel-loader": "8.2.3",
|
||||
"babel-plugin-optional-require": "0.3.1",
|
||||
|
@ -190,5 +192,9 @@
|
|||
"postinstall": "patch-package --error-on-fail && jetify",
|
||||
"validate": "npm ls",
|
||||
"start": "make dev"
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/react": "17.0.14",
|
||||
"@types/react-dom": "17.0.14"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import { SET_VOLUME } from './actionTypes';
|
|||
import {
|
||||
ContextMenuLobbyParticipantReject
|
||||
} from './components/native';
|
||||
import RoomParticipantMenu from './components/native/RoomParticipantMenu';
|
||||
export * from './actions.any';
|
||||
|
||||
/**
|
||||
|
@ -81,3 +82,17 @@ export function setVolume(participantId: string, volume: number) {
|
|||
volume
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the breakout room participant menu.
|
||||
*
|
||||
* @param {Object} room - The room the participant is in.
|
||||
* @param {string} participantJid - The jid of the participant.
|
||||
* @param {string} participantName - The display name of the participant.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function showRoomParticipantMenu(room: Object, participantJid: string, participantName: string) {
|
||||
return openSheet(RoomParticipantMenu, { room,
|
||||
participantJid,
|
||||
participantName });
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import React, { useCallback } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { isParticipantModerator } from '../../../../../base/participants';
|
||||
import { isLocalParticipantModerator, isParticipantModerator } from '../../../../../base/participants';
|
||||
import { showRoomParticipantMenu } from '../../../../actions.native';
|
||||
import ParticipantItem from '../../../native/ParticipantItem';
|
||||
|
||||
type Props = {
|
||||
|
@ -11,11 +12,23 @@ type Props = {
|
|||
/**
|
||||
* Participant to be displayed.
|
||||
*/
|
||||
item: Object
|
||||
item: Object,
|
||||
|
||||
/**
|
||||
* The room the participant is in.
|
||||
*/
|
||||
room: Object
|
||||
};
|
||||
|
||||
const BreakoutRoomParticipantItem = ({ item }: Props) => {
|
||||
const BreakoutRoomParticipantItem = ({ item, room }: Props) => {
|
||||
const { defaultRemoteDisplayName } = useSelector(state => state['features/base/config']);
|
||||
const moderator = useSelector(isLocalParticipantModerator);
|
||||
const dispatch = useDispatch();
|
||||
const onPress = useCallback(() => {
|
||||
if (moderator) {
|
||||
dispatch(showRoomParticipantMenu(room, item.jid, item.displayName));
|
||||
}
|
||||
}, [ moderator, room, item ]);
|
||||
|
||||
return (
|
||||
<ParticipantItem
|
||||
|
@ -23,6 +36,7 @@ const BreakoutRoomParticipantItem = ({ item }: Props) => {
|
|||
isKnockingParticipant = { false }
|
||||
isModerator = { isParticipantModerator(item) }
|
||||
key = { item.jid }
|
||||
onPress = { onPress }
|
||||
participantID = { item.jid } />
|
||||
);
|
||||
};
|
||||
|
|
|
@ -64,7 +64,9 @@ export const CollapsibleRoom = ({ room, searchString }: Props) => {
|
|||
keyExtractor = { _keyExtractor }
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
renderItem = { ({ item: participant }) => participantMatchesSearch(participant, searchString)
|
||||
&& <BreakoutRoomParticipantItem item = { participant } /> }
|
||||
&& <BreakoutRoomParticipantItem
|
||||
item = { participant }
|
||||
room = { room } /> }
|
||||
scrollEnabled = { true }
|
||||
showsHorizontalScrollIndicator = { false }
|
||||
windowSize = { 2 } />
|
||||
|
|
|
@ -8,8 +8,11 @@ import { useSelector } from 'react-redux';
|
|||
|
||||
import { ListItem } from '../../../../../base/components';
|
||||
import { Icon, IconArrowDown, IconArrowUp } from '../../../../../base/icons';
|
||||
import { isLocalParticipantModerator } from '../../../../../base/participants';
|
||||
import { showOverflowDrawer } from '../../../../../toolbox/functions.web';
|
||||
import { ACTION_TRIGGER } from '../../../../constants';
|
||||
import { participantMatchesSearch } from '../../../../functions';
|
||||
import ParticipantActionEllipsis from '../../../web/ParticipantActionEllipsis';
|
||||
import ParticipantItem from '../../../web/ParticipantItem';
|
||||
|
||||
type Props = {
|
||||
|
@ -39,6 +42,16 @@ type Props = {
|
|||
*/
|
||||
onLeave?: Function,
|
||||
|
||||
/**
|
||||
* The raise context for the participant menu.
|
||||
*/
|
||||
participantContextEntity: ?Object,
|
||||
|
||||
/**
|
||||
* Callback to raise participant context menu.
|
||||
*/
|
||||
raiseParticipantContextMenu: Function,
|
||||
|
||||
/**
|
||||
* Room reference.
|
||||
*/
|
||||
|
@ -47,7 +60,12 @@ type Props = {
|
|||
/**
|
||||
* Participants search string.
|
||||
*/
|
||||
searchString: string
|
||||
searchString: string,
|
||||
|
||||
/**
|
||||
* Toggles the room participant context menu.
|
||||
*/
|
||||
toggleParticipantMenu: Function
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(theme => {
|
||||
|
@ -84,8 +102,11 @@ export const CollapsibleRoom = ({
|
|||
isHighlighted,
|
||||
onRaiseMenu,
|
||||
onLeave,
|
||||
participantContextEntity,
|
||||
raiseParticipantContextMenu,
|
||||
room,
|
||||
searchString
|
||||
searchString,
|
||||
toggleParticipantMenu
|
||||
}: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const styles = useStyles();
|
||||
|
@ -97,6 +118,8 @@ export const CollapsibleRoom = ({
|
|||
onRaiseMenu(target);
|
||||
}, [ onRaiseMenu ]);
|
||||
const { defaultRemoteDisplayName } = useSelector(state => state['features/base/config']);
|
||||
const overflowDrawer = useSelector(showOverflowDrawer);
|
||||
const moderator = useSelector(isLocalParticipantModerator);
|
||||
|
||||
const arrow = (<div className = { styles.arrowContainer }>
|
||||
<Icon
|
||||
|
@ -109,6 +132,13 @@ export const CollapsibleRoom = ({
|
|||
|| {}).length})`}
|
||||
</span>);
|
||||
|
||||
const raiseParticipantMenu = useCallback(({ participantID, displayName }) => moderator
|
||||
&& raiseParticipantContextMenu({
|
||||
room,
|
||||
jid: participantID,
|
||||
participantName: displayName
|
||||
}), [ room, moderator ]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ListItem
|
||||
|
@ -126,10 +156,21 @@ export const CollapsibleRoom = ({
|
|||
&& Object.values(room?.participants || {}).map((p: Object) =>
|
||||
participantMatchesSearch(p, searchString) && (
|
||||
<ParticipantItem
|
||||
actionsTrigger = { ACTION_TRIGGER.HOVER }
|
||||
displayName = { p.displayName || defaultRemoteDisplayName }
|
||||
isHighlighted = { participantContextEntity?.jid === p.jid }
|
||||
key = { p.jid }
|
||||
local = { false }
|
||||
participantID = { p.jid } />
|
||||
openDrawerForParticipant = { raiseParticipantMenu }
|
||||
overflowDrawer = { overflowDrawer }
|
||||
participantID = { p.jid }>
|
||||
{!overflowDrawer && moderator && (
|
||||
<ParticipantActionEllipsis
|
||||
onClick = { toggleParticipantMenu({ room,
|
||||
jid: p.jid,
|
||||
participantName: p.displayName }) } />
|
||||
)}
|
||||
</ParticipantItem>
|
||||
))
|
||||
}
|
||||
</>
|
||||
|
|
|
@ -21,6 +21,7 @@ import JoinActionButton from './JoinQuickActionButton';
|
|||
import { LeaveButton } from './LeaveButton';
|
||||
import RoomActionEllipsis from './RoomActionEllipsis';
|
||||
import { RoomContextMenu } from './RoomContextMenu';
|
||||
import { RoomParticipantContextMenu } from './RoomParticipantContextMenu';
|
||||
|
||||
type Props = {
|
||||
|
||||
|
@ -41,7 +42,8 @@ export const RoomList = ({ searchString }: Props) => {
|
|||
const { hideJoinRoomButton } = useSelector(getBreakoutRoomsConfig);
|
||||
const _overflowDrawer = useSelector(showOverflowDrawer);
|
||||
const [ lowerMenu, raiseMenu, toggleMenu, menuEnter, menuLeave, raiseContext ] = useContextMenu();
|
||||
|
||||
const [ lowerParticipantMenu, raiseParticipantMenu, toggleParticipantMenu,
|
||||
participantMenuEnter, participantMenuLeave, raiseParticipantContext ] = useContextMenu();
|
||||
const onRaiseMenu = useCallback(room => target => raiseMenu(room, target), [ raiseMenu ]);
|
||||
|
||||
return (
|
||||
|
@ -55,8 +57,11 @@ export const RoomList = ({ searchString }: Props) => {
|
|||
isHighlighted = { raiseContext.entity === room }
|
||||
onLeave = { lowerMenu }
|
||||
onRaiseMenu = { onRaiseMenu(room) }
|
||||
participantContextEntity = { raiseParticipantContext.entity }
|
||||
raiseParticipantContextMenu = { raiseParticipantMenu }
|
||||
room = { room }
|
||||
searchString = { searchString }>
|
||||
searchString = { searchString }
|
||||
toggleParticipantMenu = { toggleParticipantMenu }>
|
||||
{!_overflowDrawer && <>
|
||||
{!hideJoinRoomButton && <JoinActionButton room = { room } />}
|
||||
{isLocalModerator && !room.isMainRoom
|
||||
|
@ -71,6 +76,11 @@ export const RoomList = ({ searchString }: Props) => {
|
|||
onLeave = { menuLeave }
|
||||
onSelect = { lowerMenu }
|
||||
{ ...raiseContext } />
|
||||
<RoomParticipantContextMenu
|
||||
onEnter = { participantMenuEnter }
|
||||
onLeave = { participantMenuLeave }
|
||||
onSelect = { lowerParticipantMenu }
|
||||
{ ...raiseParticipantContext } />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
import { makeStyles } from '@material-ui/core';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
// @ts-ignore
|
||||
import { Avatar } from '../../../../../base/avatar';
|
||||
// @ts-ignore
|
||||
import { ContextMenu, ContextMenuItemGroup } from '../../../../../base/components';
|
||||
// @ts-ignore
|
||||
import { isLocalParticipantModerator } from '../../../../../base/participants';
|
||||
// @ts-ignore
|
||||
import { getBreakoutRooms } from '../../../../../breakout-rooms/functions';
|
||||
// @ts-ignore
|
||||
import { showOverflowDrawer } from '../../../../../toolbox/functions.web';
|
||||
// @ts-ignore
|
||||
import SendToRoomButton from '../../../../../video-menu/components/web/SendToRoomButton';
|
||||
// @ts-ignore
|
||||
import { AVATAR_SIZE } from '../../../../constants';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Room and participant jid reference.
|
||||
*/
|
||||
entity: {
|
||||
room: any,
|
||||
jid: string,
|
||||
participantName: string
|
||||
},
|
||||
|
||||
/**
|
||||
* Target elements against which positioning calculations are made.
|
||||
*/
|
||||
offsetTarget: HTMLElement|undefined,
|
||||
|
||||
/**
|
||||
* Callback for the mouse entering the component.
|
||||
*/
|
||||
onEnter: Function,
|
||||
|
||||
/**
|
||||
* Callback for the mouse leaving the component.
|
||||
*/
|
||||
onLeave: Function,
|
||||
|
||||
/**
|
||||
* Callback for making a selection in the menu.
|
||||
*/
|
||||
onSelect: Function
|
||||
};
|
||||
|
||||
const useStyles = makeStyles((theme:any) => {
|
||||
return {
|
||||
text: {
|
||||
color: theme.palette.text02,
|
||||
padding: '10px 16px',
|
||||
height: '40px',
|
||||
overflow: 'hidden',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
boxSizing: 'border-box'
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
export const RoomParticipantContextMenu = ({
|
||||
entity,
|
||||
offsetTarget,
|
||||
onEnter,
|
||||
onLeave,
|
||||
onSelect
|
||||
}: Props) => {
|
||||
const styles = useStyles();
|
||||
const { t } = useTranslation();
|
||||
const isLocalModerator = useSelector(isLocalParticipantModerator);
|
||||
const lowerMenu = useCallback(() => onSelect(true), [onSelect]);
|
||||
const rooms: Object = useSelector(getBreakoutRooms);
|
||||
const overflowDrawer = useSelector(showOverflowDrawer);
|
||||
|
||||
const breakoutRoomsButtons = useMemo(() => Object.values(rooms || {}).map((room: any) => {
|
||||
if (room.id !== entity?.room?.id) {
|
||||
return (<SendToRoomButton
|
||||
key = { room.id }
|
||||
onClick = { lowerMenu }
|
||||
participantID = { entity?.jid }
|
||||
room = { room } />);
|
||||
}
|
||||
|
||||
return null;
|
||||
}).filter(Boolean), [ entity, rooms ]);
|
||||
|
||||
return isLocalModerator && (
|
||||
<ContextMenu
|
||||
entity = { entity }
|
||||
isDrawerOpen = { Boolean(entity) }
|
||||
offsetTarget = { offsetTarget }
|
||||
onClick = { lowerMenu }
|
||||
onDrawerClose = { onSelect }
|
||||
onMouseEnter = { onEnter }
|
||||
onMouseLeave = { onLeave }>
|
||||
{overflowDrawer && entity?.jid && <ContextMenuItemGroup
|
||||
actions = { [ {
|
||||
accessibilityLabel: entity?.participantName,
|
||||
customIcon: <Avatar
|
||||
displayName = { entity?.participantName }
|
||||
size = { AVATAR_SIZE } />,
|
||||
text: entity?.participantName
|
||||
} ] } />}
|
||||
<ContextMenuItemGroup>
|
||||
<div className = { styles.text }>
|
||||
{t('breakoutRooms.actions.sendToBreakoutRoom')}
|
||||
</div>
|
||||
{breakoutRoomsButtons}
|
||||
</ContextMenuItemGroup>
|
||||
</ContextMenu>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,147 @@
|
|||
import React, { PureComponent } from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
|
||||
// @ts-ignore
|
||||
import { Avatar } from '../../../base/avatar';
|
||||
// @ts-ignore
|
||||
import { BottomSheet, hideSheet } from '../../../base/dialog';
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { getBreakoutRooms } from '../../../breakout-rooms/functions';
|
||||
import SendToBreakoutRoom from '../../../video-menu/components/native/SendToBreakoutRoom';
|
||||
import styles from '../../../video-menu/components/native/styles';
|
||||
import { bottomSheetStyles } from '../../../base/dialog/components/native/styles';
|
||||
|
||||
/**
|
||||
* Size of the rendered avatar in the menu.
|
||||
*/
|
||||
const AVATAR_SIZE = 24;
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The list of all breakout rooms.
|
||||
*/
|
||||
_rooms: Array<any>,
|
||||
|
||||
/**
|
||||
* The room the participant is in.
|
||||
*/
|
||||
room: any,
|
||||
|
||||
/**
|
||||
* The jid of the selected participant.
|
||||
*/
|
||||
participantJid: string,
|
||||
|
||||
/**
|
||||
* The display name of the selected participant.
|
||||
*/
|
||||
participantName: string,
|
||||
|
||||
/**
|
||||
* The Redux dispatch function.
|
||||
*/
|
||||
dispatch: Function,
|
||||
|
||||
/**
|
||||
* Translation function.
|
||||
*/
|
||||
t: Function
|
||||
}
|
||||
|
||||
/**
|
||||
* Class to implement a popup menu that opens upon long pressing a thumbnail.
|
||||
*/
|
||||
class RoomParticipantMenu extends PureComponent<Props> {
|
||||
/**
|
||||
* Constructor of the component.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this._onCancel = this._onCancel.bind(this);
|
||||
this._renderMenuHeader = this._renderMenuHeader.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements {@code Component#render}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
const { _rooms, participantJid, room, t } = this.props;
|
||||
const buttonProps = {
|
||||
afterClick: this._onCancel,
|
||||
showLabel: true,
|
||||
participantID: participantJid,
|
||||
styles: bottomSheetStyles.buttons
|
||||
};
|
||||
|
||||
return (
|
||||
<BottomSheet
|
||||
renderHeader = { this._renderMenuHeader }
|
||||
showSlidingView = { true }>
|
||||
<View style = { styles.contextMenuItem }>
|
||||
<Text style = { styles.contextMenuItemText }>
|
||||
{t('breakoutRooms.actions.sendToBreakoutRoom')}
|
||||
</Text>
|
||||
</View>
|
||||
{_rooms.map(r => room.id !== r.id && (<SendToBreakoutRoom
|
||||
key = { r.id }
|
||||
room = { r }
|
||||
{ ...buttonProps } />))}
|
||||
</BottomSheet>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to hide the {@code RemoteVideoMenu}.
|
||||
*
|
||||
* @private
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_onCancel() {
|
||||
this.props.dispatch(hideSheet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to render the menu's header.
|
||||
*
|
||||
* @returns {React$Element}
|
||||
*/
|
||||
_renderMenuHeader() {
|
||||
const { participantName } = this.props;
|
||||
|
||||
return (
|
||||
<View
|
||||
style = { [
|
||||
bottomSheetStyles.sheet,
|
||||
styles.participantNameContainer ] }>
|
||||
<Avatar
|
||||
displayName = { participantName }
|
||||
size = { AVATAR_SIZE } />
|
||||
<Text style = { styles.participantNameLabel }>
|
||||
{ participantName }
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function that maps parts of Redux state tree into component props.
|
||||
*
|
||||
* @param {Object} state - Redux state.
|
||||
* @private
|
||||
* @returns {Props}
|
||||
*/
|
||||
function _mapStateToProps(state) {
|
||||
return {
|
||||
_rooms: Object.values(getBreakoutRooms(state))
|
||||
};
|
||||
}
|
||||
|
||||
export default translate(connect(_mapStateToProps)(RoomParticipantMenu));
|
|
@ -107,3 +107,8 @@ export const VideoStateIcons = {
|
|||
),
|
||||
[MEDIA_STATE.NONE]: null
|
||||
};
|
||||
|
||||
/**
|
||||
* Mobile web context menu avatar size.
|
||||
*/
|
||||
export const AVATAR_SIZE = 20;
|
||||
|
|
|
@ -9,7 +9,8 @@ import { KICK_OUT_ENABLED, getFeatureFlag } from '../../../base/flags';
|
|||
import { translate } from '../../../base/i18n';
|
||||
import {
|
||||
getParticipantById,
|
||||
getParticipantDisplayName
|
||||
getParticipantDisplayName,
|
||||
isLocalParticipantModerator
|
||||
} from '../../../base/participants';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { getBreakoutRooms, getCurrentRoomId } from '../../../breakout-rooms/functions';
|
||||
|
@ -76,6 +77,11 @@ type Props = {
|
|||
*/
|
||||
_isParticipantAvailable?: boolean,
|
||||
|
||||
/**
|
||||
* Whether the local participant is moderator or not.
|
||||
*/
|
||||
_moderator: boolean,
|
||||
|
||||
/**
|
||||
* Display name of the participant retrieved from Redux.
|
||||
*/
|
||||
|
@ -120,6 +126,7 @@ class RemoteVideoMenu extends PureComponent<Props> {
|
|||
_disableRemoteMute,
|
||||
_disableGrantModerator,
|
||||
_isParticipantAvailable,
|
||||
_moderator,
|
||||
_rooms,
|
||||
_currentRoomId,
|
||||
participantId,
|
||||
|
@ -148,7 +155,7 @@ class RemoteVideoMenu extends PureComponent<Props> {
|
|||
<ConnectionStatusButton
|
||||
{ ...buttonProps }
|
||||
afterClick = { undefined } />
|
||||
{_rooms.length > 1 && <>
|
||||
{_moderator && _rooms.length > 1 && <>
|
||||
<Divider style = { styles.divider } />
|
||||
<View style = { styles.contextMenuItem }>
|
||||
<Text style = { styles.contextMenuItemText }>
|
||||
|
@ -216,6 +223,7 @@ function _mapStateToProps(state, ownProps) {
|
|||
const _rooms = Object.values(getBreakoutRooms(state));
|
||||
const _currentRoomId = getCurrentRoomId(state);
|
||||
const shouldDisableKick = disableKick || !kickOutEnabled;
|
||||
const moderator = isLocalParticipantModerator(state);
|
||||
|
||||
return {
|
||||
_currentRoomId,
|
||||
|
@ -223,6 +231,7 @@ function _mapStateToProps(state, ownProps) {
|
|||
_disableRemoteMute: Boolean(disableRemoteMute),
|
||||
_disablePrivateChat: Boolean(disablePrivateChat),
|
||||
_isParticipantAvailable: Boolean(isParticipantAvailable),
|
||||
_moderator: moderator,
|
||||
_participantDisplayName: getParticipantDisplayName(state, participantId),
|
||||
_rooms
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue