fix(shared-video,video-menu) add ability to stop shared video from video menu
Specifically, in the bottom sheet (on mobile) and participants pane.
This commit is contained in:
parent
e421a119e1
commit
3c2ad24652
|
@ -13,7 +13,8 @@ import {
|
|||
getParticipantCount,
|
||||
isEveryoneModerator,
|
||||
pinParticipant,
|
||||
getParticipantByIdOrUndefined
|
||||
getParticipantByIdOrUndefined,
|
||||
getLocalParticipant
|
||||
} from '../../../base/participants';
|
||||
import { Container } from '../../../base/react';
|
||||
import { connect } from '../../../base/redux';
|
||||
|
@ -24,6 +25,8 @@ import { DisplayNameLabel } from '../../../display-name';
|
|||
import { toggleToolboxVisible } from '../../../toolbox/actions.native';
|
||||
import { RemoteVideoMenu } from '../../../video-menu';
|
||||
import ConnectionStatusComponent from '../../../video-menu/components/native/ConnectionStatusComponent';
|
||||
import SharedVideoMenu
|
||||
from '../../../video-menu/components/native/SharedVideoMenu';
|
||||
|
||||
import AudioMutedIndicator from './AudioMutedIndicator';
|
||||
import DominantSpeakerIndicator from './DominantSpeakerIndicator';
|
||||
|
@ -48,6 +51,11 @@ type Props = {
|
|||
*/
|
||||
_largeVideo: Object,
|
||||
|
||||
/**
|
||||
* Shared video local participant owner.
|
||||
*/
|
||||
_localVideoOwner: boolean,
|
||||
|
||||
/**
|
||||
* The Redux representation of the participant to display.
|
||||
*/
|
||||
|
@ -116,6 +124,7 @@ function Thumbnail(props: Props) {
|
|||
const {
|
||||
_audioMuted: audioMuted,
|
||||
_largeVideo: largeVideo,
|
||||
_localVideoOwner,
|
||||
_renderDominantSpeakerIndicator: renderDominantSpeakerIndicator,
|
||||
_renderModeratorIndicator: renderModeratorIndicator,
|
||||
_participant: participant,
|
||||
|
@ -144,11 +153,19 @@ function Thumbnail(props: Props) {
|
|||
dispatch(openDialog(ConnectionStatusComponent, {
|
||||
participantID: participant.id
|
||||
}));
|
||||
} else {
|
||||
dispatch(openDialog(RemoteVideoMenu, {
|
||||
participant
|
||||
}));
|
||||
} else if (participant.isFakeParticipant) {
|
||||
if (_localVideoOwner) {
|
||||
dispatch(openDialog(SharedVideoMenu, {
|
||||
participant
|
||||
}));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
dispatch(openDialog(RemoteVideoMenu, {
|
||||
participant
|
||||
}));
|
||||
}, [ participant, dispatch ]);
|
||||
|
||||
return (
|
||||
|
@ -223,9 +240,11 @@ function _mapStateToProps(state, ownProps) {
|
|||
// filmstrip doesn't render the video of the participant who is rendered on
|
||||
// the stage i.e. as a large video.
|
||||
const largeVideo = state['features/large-video'];
|
||||
const { ownerId } = state['features/shared-video'];
|
||||
const tracks = state['features/base/tracks'];
|
||||
const { participantID } = ownProps;
|
||||
const participant = getParticipantByIdOrUndefined(state, participantID);
|
||||
const localParticipantId = getLocalParticipant(state).id;
|
||||
const id = participant?.id;
|
||||
const audioTrack
|
||||
= getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.AUDIO, id);
|
||||
|
@ -240,6 +259,7 @@ function _mapStateToProps(state, ownProps) {
|
|||
return {
|
||||
_audioMuted: audioTrack?.muted ?? true,
|
||||
_largeVideo: largeVideo,
|
||||
_localVideoOwner: Boolean(ownerId === localParticipantId),
|
||||
_participant: participant,
|
||||
_renderDominantSpeakerIndicator: renderDominantSpeakerIndicator,
|
||||
_renderModeratorIndicator: renderModeratorIndicator,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// @flow
|
||||
|
||||
import { openDialog } from '../base/dialog';
|
||||
import { SharedVideoMenu } from '../video-menu';
|
||||
import ConnectionStatusComponent
|
||||
from '../video-menu/components/native/ConnectionStatusComponent';
|
||||
import RemoteVideoMenu from '../video-menu/components/native/RemoteVideoMenu';
|
||||
|
@ -42,6 +43,16 @@ export function showContextMenuDetails(participant: Object) {
|
|||
return openDialog(RemoteVideoMenu, { participant });
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the shared video menu.
|
||||
*
|
||||
* @param {Object} participant - The selected meeting participant.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function showSharedVideoMenu(participant: Object) {
|
||||
return openDialog(SharedVideoMenu, { participant });
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the volume.
|
||||
*
|
||||
|
|
|
@ -6,20 +6,34 @@ import { Text, View } from 'react-native';
|
|||
import { Button } from 'react-native-paper';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
|
||||
import { Icon, IconInviteMore } from '../../../base/icons';
|
||||
import {
|
||||
getLocalParticipant,
|
||||
getParticipantCountWithFake,
|
||||
getRemoteParticipants
|
||||
} from '../../../base/participants';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { doInvitePeople } from '../../../invite/actions.native';
|
||||
import { showConnectionStatus, showContextMenuDetails } from '../../actions.native';
|
||||
import {
|
||||
showConnectionStatus,
|
||||
showContextMenuDetails,
|
||||
showSharedVideoMenu
|
||||
} from '../../actions.native';
|
||||
import { shouldRenderInviteButton } from '../../functions';
|
||||
|
||||
import MeetingParticipantItem from './MeetingParticipantItem';
|
||||
import styles from './styles';
|
||||
|
||||
export const MeetingParticipantList = () => {
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Shared video local participant owner.
|
||||
*/
|
||||
_localVideoOwner: boolean
|
||||
}
|
||||
|
||||
const MeetingParticipantList = ({ _localVideoOwner }: Props) => {
|
||||
const dispatch = useDispatch();
|
||||
const items = [];
|
||||
const localParticipant = useSelector(getLocalParticipant);
|
||||
|
@ -30,14 +44,34 @@ export const MeetingParticipantList = () => {
|
|||
const { t } = useTranslation();
|
||||
|
||||
// eslint-disable-next-line react/no-multi-comp
|
||||
const renderParticipant = p => (
|
||||
<MeetingParticipantItem
|
||||
key = { p.id }
|
||||
/* eslint-disable-next-line react/jsx-no-bind,no-confusing-arrow */
|
||||
onPress = { () => p.local
|
||||
? dispatch(showConnectionStatus(p.id)) : dispatch(showContextMenuDetails(p)) }
|
||||
participantID = { p.id } />
|
||||
);
|
||||
const renderParticipant = p => {
|
||||
if (p.isFakeParticipant) {
|
||||
if (_localVideoOwner) {
|
||||
return (
|
||||
<MeetingParticipantItem
|
||||
key = { p.id }
|
||||
/* eslint-disable-next-line react/jsx-no-bind,no-confusing-arrow */
|
||||
onPress = { () => dispatch(showSharedVideoMenu(p)) }
|
||||
participantID = { p.id } />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<MeetingParticipantItem
|
||||
key = { p.id }
|
||||
participantID = { p.id } />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<MeetingParticipantItem
|
||||
key = { p.id }
|
||||
/* eslint-disable-next-line react/jsx-no-bind,no-confusing-arrow */
|
||||
onPress = { () => p.local
|
||||
? dispatch(showConnectionStatus(p.id)) : dispatch(showContextMenuDetails(p)) }
|
||||
participantID = { p.id } />
|
||||
);
|
||||
};
|
||||
|
||||
items.push(renderParticipant(localParticipant));
|
||||
|
||||
|
@ -71,3 +105,20 @@ export const MeetingParticipantList = () => {
|
|||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Maps (parts of) the redux state to the associated props for this component.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @private
|
||||
* @returns {Props}
|
||||
*/
|
||||
function _mapStateToProps(state): Object {
|
||||
const { ownerId } = state['features/shared-video'];
|
||||
const localParticipantId = getLocalParticipant(state).id;
|
||||
|
||||
return {
|
||||
_localVideoOwner: Boolean(ownerId === localParticipantId)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(_mapStateToProps)(MeetingParticipantList);
|
||||
|
|
|
@ -19,7 +19,7 @@ import { close } from '../../actions.native';
|
|||
|
||||
import { ContextMenuMore } from './ContextMenuMore';
|
||||
import LobbyParticipantList from './LobbyParticipantList';
|
||||
import { MeetingParticipantList } from './MeetingParticipantList';
|
||||
import MeetingParticipantList from './MeetingParticipantList';
|
||||
import styles from './styles';
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// @flow
|
||||
|
||||
export { default as MeetingParticipantList } from './MeetingParticipantList';
|
||||
export { default as ParticipantsPane } from './ParticipantsPane';
|
||||
export { default as ParticipantsPaneButton } from './ParticipantsPaneButton';
|
||||
export { default as ContextMenuLobbyParticipantReject } from './ContextMenuLobbyParticipantReject';
|
||||
|
|
|
@ -11,9 +11,11 @@ import {
|
|||
IconMessage,
|
||||
IconMicDisabled,
|
||||
IconMuteEveryoneElse,
|
||||
IconShareVideo,
|
||||
IconVideoOff
|
||||
} from '../../../base/icons';
|
||||
import {
|
||||
getLocalParticipant,
|
||||
getParticipantByIdOrUndefined,
|
||||
isLocalParticipantModerator,
|
||||
isParticipantModerator
|
||||
|
@ -21,6 +23,7 @@ import {
|
|||
import { connect } from '../../../base/redux';
|
||||
import { isParticipantAudioMuted, isParticipantVideoMuted } from '../../../base/tracks';
|
||||
import { openChat } from '../../../chat/actions';
|
||||
import { stopSharedVideo } from '../../../shared-video/actions.any';
|
||||
import { GrantModeratorDialog, KickRemoteParticipantDialog, MuteEveryoneDialog } from '../../../video-menu';
|
||||
import MuteRemoteParticipantsVideoDialog from '../../../video-menu/components/web/MuteRemoteParticipantsVideoDialog';
|
||||
import { getComputedOuterHeight } from '../../functions';
|
||||
|
@ -60,6 +63,11 @@ type Props = {
|
|||
*/
|
||||
_isParticipantAudioMuted: boolean,
|
||||
|
||||
/**
|
||||
* Shared video local participant owner.
|
||||
*/
|
||||
_localVideoOwner: boolean,
|
||||
|
||||
/**
|
||||
* Participant reference
|
||||
*/
|
||||
|
@ -143,6 +151,7 @@ class MeetingParticipantContextMenu extends Component<Props, State> {
|
|||
this._onMuteEveryoneElse = this._onMuteEveryoneElse.bind(this);
|
||||
this._onMuteVideo = this._onMuteVideo.bind(this);
|
||||
this._onSendPrivateMessage = this._onSendPrivateMessage.bind(this);
|
||||
this._onStopSharedVideo = this._onStopSharedVideo.bind(this);
|
||||
this._position = this._position.bind(this);
|
||||
}
|
||||
|
||||
|
@ -176,6 +185,19 @@ class MeetingParticipantContextMenu extends Component<Props, State> {
|
|||
}));
|
||||
}
|
||||
|
||||
_onStopSharedVideo: () => void;
|
||||
|
||||
/**
|
||||
* Stops shared video.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onStopSharedVideo() {
|
||||
const { dispatch } = this.props;
|
||||
|
||||
dispatch(stopSharedVideo());
|
||||
}
|
||||
|
||||
_onMuteEveryoneElse: () => void;
|
||||
|
||||
/**
|
||||
|
@ -282,6 +304,7 @@ class MeetingParticipantContextMenu extends Component<Props, State> {
|
|||
_isParticipantModerator,
|
||||
_isParticipantVideoMuted,
|
||||
_isParticipantAudioMuted,
|
||||
_localVideoOwner,
|
||||
_participant,
|
||||
onEnter,
|
||||
onLeave,
|
||||
|
@ -302,66 +325,81 @@ class MeetingParticipantContextMenu extends Component<Props, State> {
|
|||
onClick = { onSelect }
|
||||
onMouseEnter = { onEnter }
|
||||
onMouseLeave = { onLeave }>
|
||||
<ContextMenuItemGroup>
|
||||
{
|
||||
_isLocalModerator && (
|
||||
<>
|
||||
{
|
||||
!_participant.isFakeParticipant && (
|
||||
<>
|
||||
<ContextMenuItemGroup>
|
||||
{
|
||||
!_isParticipantAudioMuted
|
||||
&& <ContextMenuItem onClick = { muteAudio(_participant) }>
|
||||
<ContextMenuIcon src = { IconMicDisabled } />
|
||||
<span>{t('dialog.muteParticipantButton')}</span>
|
||||
</ContextMenuItem>
|
||||
_isLocalModerator && (
|
||||
<>
|
||||
{
|
||||
!_isParticipantAudioMuted
|
||||
&& <ContextMenuItem onClick = { muteAudio(_participant) }>
|
||||
<ContextMenuIcon src = { IconMicDisabled } />
|
||||
<span>{t('dialog.muteParticipantButton')}</span>
|
||||
</ContextMenuItem>
|
||||
}
|
||||
|
||||
<ContextMenuItem onClick = { this._onMuteEveryoneElse }>
|
||||
<ContextMenuIcon src = { IconMuteEveryoneElse } />
|
||||
<span>{t('toolbar.accessibilityLabel.muteEveryoneElse')}</span>
|
||||
</ContextMenuItem>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
<ContextMenuItem onClick = { this._onMuteEveryoneElse }>
|
||||
<ContextMenuIcon src = { IconMuteEveryoneElse } />
|
||||
<span>{t('toolbar.accessibilityLabel.muteEveryoneElse')}</span>
|
||||
</ContextMenuItem>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
_isLocalModerator && (
|
||||
_isParticipantVideoMuted || (
|
||||
<ContextMenuItem onClick = { this._onMuteVideo }>
|
||||
<ContextMenuIcon src = { IconVideoOff } />
|
||||
<span>{t('participantsPane.actions.stopVideo')}</span>
|
||||
</ContextMenuItem>
|
||||
)
|
||||
)
|
||||
}
|
||||
</ContextMenuItemGroup>
|
||||
|
||||
<ContextMenuItemGroup>
|
||||
{
|
||||
_isLocalModerator && (
|
||||
<>
|
||||
{
|
||||
!_isParticipantModerator && (
|
||||
<ContextMenuItem onClick = { this._onGrantModerator }>
|
||||
<ContextMenuIcon src = { IconCrown } />
|
||||
<span>{t('toolbar.accessibilityLabel.grantModerator')}</span>
|
||||
_isLocalModerator && (
|
||||
_isParticipantVideoMuted || (
|
||||
<ContextMenuItem onClick = { this._onMuteVideo }>
|
||||
<ContextMenuIcon src = { IconVideoOff } />
|
||||
<span>{t('participantsPane.actions.stopVideo')}</span>
|
||||
</ContextMenuItem>
|
||||
)
|
||||
)
|
||||
}
|
||||
</ContextMenuItemGroup>
|
||||
|
||||
<ContextMenuItemGroup>
|
||||
{
|
||||
_isLocalModerator && (
|
||||
<>
|
||||
{
|
||||
!_isParticipantModerator && (
|
||||
<ContextMenuItem onClick = { this._onGrantModerator }>
|
||||
<ContextMenuIcon src = { IconCrown } />
|
||||
<span>{t('toolbar.accessibilityLabel.grantModerator')}</span>
|
||||
</ContextMenuItem>
|
||||
)
|
||||
}
|
||||
<ContextMenuItem onClick = { this._onKick }>
|
||||
<ContextMenuIcon src = { IconCloseCircle } />
|
||||
<span>{ t('videothumbnail.kick') }</span>
|
||||
</ContextMenuItem>
|
||||
</>
|
||||
)
|
||||
}
|
||||
{
|
||||
_isChatButtonEnabled && (
|
||||
<ContextMenuItem onClick = { this._onSendPrivateMessage }>
|
||||
<ContextMenuIcon src = { IconMessage } />
|
||||
<span>{t('toolbar.accessibilityLabel.privateMessage')}</span>
|
||||
</ContextMenuItem>
|
||||
)
|
||||
}
|
||||
<ContextMenuItem onClick = { this._onKick }>
|
||||
<ContextMenuIcon src = { IconCloseCircle } />
|
||||
<span>{ t('videothumbnail.kick') }</span>
|
||||
</ContextMenuItem>
|
||||
</>
|
||||
)
|
||||
}
|
||||
{
|
||||
_isChatButtonEnabled && (
|
||||
<ContextMenuItem onClick = { this._onSendPrivateMessage }>
|
||||
<ContextMenuIcon src = { IconMessage } />
|
||||
<span>{t('toolbar.accessibilityLabel.privateMessage')}</span>
|
||||
</ContextMenuItem>
|
||||
)
|
||||
}
|
||||
</ContextMenuItemGroup>
|
||||
</ContextMenuItemGroup>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
_participant.isFakeParticipant && _localVideoOwner && (
|
||||
<ContextMenuItem onClick = { this._onStopSharedVideo }>
|
||||
<ContextMenuIcon src = { IconShareVideo } />
|
||||
<span>{t('toolbar.stopSharedVideo')}</span>
|
||||
</ContextMenuItem>
|
||||
)
|
||||
}
|
||||
</ContextMenu>
|
||||
);
|
||||
}
|
||||
|
@ -377,7 +415,8 @@ class MeetingParticipantContextMenu extends Component<Props, State> {
|
|||
*/
|
||||
function _mapStateToProps(state, ownProps): Object {
|
||||
const { participantID } = ownProps;
|
||||
|
||||
const { ownerId } = state['features/shared-video'];
|
||||
const localParticipantId = getLocalParticipant(state).id;
|
||||
const participant = getParticipantByIdOrUndefined(state, participantID);
|
||||
|
||||
const _isLocalModerator = isLocalParticipantModerator(state);
|
||||
|
@ -392,6 +431,7 @@ function _mapStateToProps(state, ownProps): Object {
|
|||
_isParticipantModerator,
|
||||
_isParticipantVideoMuted,
|
||||
_isParticipantAudioMuted,
|
||||
_localVideoOwner: Boolean(ownerId === localParticipantId),
|
||||
_participant: participant
|
||||
};
|
||||
}
|
||||
|
|
|
@ -2,7 +2,11 @@
|
|||
|
||||
import React from 'react';
|
||||
|
||||
import { getParticipantByIdOrUndefined, getParticipantDisplayName } from '../../../base/participants';
|
||||
import {
|
||||
getLocalParticipant,
|
||||
getParticipantByIdOrUndefined,
|
||||
getParticipantDisplayName
|
||||
} from '../../../base/participants';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { isParticipantAudioMuted, isParticipantVideoMuted } from '../../../base/tracks';
|
||||
import { ACTION_TRIGGER, MEDIA_STATE, type MediaState } from '../../constants';
|
||||
|
@ -34,6 +38,16 @@ type Props = {
|
|||
*/
|
||||
_local: boolean,
|
||||
|
||||
/**
|
||||
* Shared video local participant owner.
|
||||
*/
|
||||
_localVideoOwner: boolean,
|
||||
|
||||
/**
|
||||
* The participant.
|
||||
*/
|
||||
_participant: Object,
|
||||
|
||||
/**
|
||||
* The participant ID.
|
||||
*
|
||||
|
@ -108,7 +122,9 @@ function MeetingParticipantItem({
|
|||
_audioMediaState,
|
||||
_displayName,
|
||||
_isVideoMuted,
|
||||
_localVideoOwner,
|
||||
_local,
|
||||
_participant,
|
||||
_participantID,
|
||||
_quickActionButtonType,
|
||||
_raisedHand,
|
||||
|
@ -133,15 +149,28 @@ function MeetingParticipantItem({
|
|||
raisedHand = { _raisedHand }
|
||||
videoMuteState = { _isVideoMuted ? MEDIA_STATE.MUTED : MEDIA_STATE.UNMUTED }
|
||||
youText = { youText }>
|
||||
<ParticipantQuickAction
|
||||
askUnmuteText = { askUnmuteText }
|
||||
buttonType = { _quickActionButtonType }
|
||||
muteAudio = { muteAudio }
|
||||
muteParticipantButtonText = { muteParticipantButtonText }
|
||||
participantID = { _participantID } />
|
||||
<ParticipantActionEllipsis
|
||||
aria-label = { participantActionEllipsisLabel }
|
||||
onClick = { onContextMenu } />
|
||||
{
|
||||
!_participant.isFakeParticipant && (
|
||||
<>
|
||||
<ParticipantQuickAction
|
||||
askUnmuteText = { askUnmuteText }
|
||||
buttonType = { _quickActionButtonType }
|
||||
muteAudio = { muteAudio }
|
||||
muteParticipantButtonText = { muteParticipantButtonText }
|
||||
participantID = { _participantID } />
|
||||
<ParticipantActionEllipsis
|
||||
aria-label = { participantActionEllipsisLabel }
|
||||
onClick = { onContextMenu } />
|
||||
</>
|
||||
)
|
||||
}
|
||||
{
|
||||
_participant.isFakeParticipant && _localVideoOwner && (
|
||||
<ParticipantActionEllipsis
|
||||
aria-label = { participantActionEllipsisLabel }
|
||||
onClick = { onContextMenu } />
|
||||
)
|
||||
}
|
||||
</ParticipantItem>
|
||||
);
|
||||
}
|
||||
|
@ -156,6 +185,8 @@ function MeetingParticipantItem({
|
|||
*/
|
||||
function _mapStateToProps(state, ownProps): Object {
|
||||
const { participantID } = ownProps;
|
||||
const { ownerId } = state['features/shared-video'];
|
||||
const localParticipantId = getLocalParticipant(state).id;
|
||||
|
||||
const participant = getParticipantByIdOrUndefined(state, participantID);
|
||||
|
||||
|
@ -170,6 +201,8 @@ function _mapStateToProps(state, ownProps): Object {
|
|||
_isAudioMuted,
|
||||
_isVideoMuted,
|
||||
_local: Boolean(participant?.local),
|
||||
_localVideoOwner: Boolean(ownerId === localParticipantId),
|
||||
_participant: participant,
|
||||
_participantID: participant?.id,
|
||||
_quickActionButtonType,
|
||||
_raisedHand: Boolean(participant?.raisedHand)
|
||||
|
|
|
@ -235,7 +235,6 @@ export const ParticipantActionsHover = styled(ParticipantActions)`
|
|||
position: absolute;
|
||||
top: 0;
|
||||
transform: translateX(-100%);
|
||||
width: 40px;
|
||||
}
|
||||
`;
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// @flow
|
||||
import { hideDialog } from '../base/dialog';
|
||||
|
||||
import { RemoteVideoMenu } from './components/native';
|
||||
import { RemoteVideoMenu, SharedVideoMenu } from './components/native';
|
||||
|
||||
/**
|
||||
* Hides the remote video menu.
|
||||
|
@ -12,4 +12,13 @@ export function hideRemoteVideoMenu() {
|
|||
return hideDialog(RemoteVideoMenu);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the shared video menu.
|
||||
*
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function hideSharedVideoMenu() {
|
||||
return hideDialog(SharedVideoMenu);
|
||||
}
|
||||
|
||||
export * from './actions.any';
|
||||
|
|
|
@ -0,0 +1,180 @@
|
|||
// @flow
|
||||
|
||||
import React, { PureComponent } from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
import { Divider } from 'react-native-paper';
|
||||
|
||||
import { Avatar } from '../../../base/avatar';
|
||||
import { ColorSchemeRegistry } from '../../../base/color-scheme';
|
||||
import { BottomSheet, isDialogOpen } from '../../../base/dialog';
|
||||
import {
|
||||
getParticipantById,
|
||||
getParticipantDisplayName
|
||||
} from '../../../base/participants';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { StyleType } from '../../../base/styles';
|
||||
import { SharedVideoButton } from '../../../shared-video/components';
|
||||
import { hideSharedVideoMenu } from '../../actions.native';
|
||||
|
||||
import styles from './styles';
|
||||
|
||||
|
||||
/**
|
||||
* Size of the rendered avatar in the menu.
|
||||
*/
|
||||
const AVATAR_SIZE = 24;
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The Redux dispatch function.
|
||||
*/
|
||||
dispatch: Function,
|
||||
|
||||
/**
|
||||
* The participant for which this menu opened for.
|
||||
*/
|
||||
participant: Object,
|
||||
|
||||
/**
|
||||
* The color-schemed stylesheet of the BottomSheet.
|
||||
*/
|
||||
_bottomSheetStyles: StyleType,
|
||||
|
||||
/**
|
||||
* True if the menu is currently open, false otherwise.
|
||||
*/
|
||||
_isOpen: boolean,
|
||||
|
||||
/**
|
||||
* Whether the participant is present in the room or not.
|
||||
*/
|
||||
_isParticipantAvailable?: boolean,
|
||||
|
||||
/**
|
||||
* Display name of the participant retrieved from Redux.
|
||||
*/
|
||||
_participantDisplayName: string,
|
||||
|
||||
/**
|
||||
* The ID of the participant.
|
||||
*/
|
||||
_participantID: ?string,
|
||||
}
|
||||
|
||||
// eslint-disable-next-line prefer-const
|
||||
let SharedVideoMenu_;
|
||||
|
||||
/**
|
||||
* Class to implement a popup menu that opens upon long pressing a fake participant thumbnail.
|
||||
*/
|
||||
class SharedVideoMenu 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 {
|
||||
_isParticipantAvailable,
|
||||
participant
|
||||
} = this.props;
|
||||
|
||||
const buttonProps = {
|
||||
afterClick: this._onCancel,
|
||||
showLabel: true,
|
||||
participantID: participant.id,
|
||||
styles: this.props._bottomSheetStyles.buttons
|
||||
};
|
||||
|
||||
return (
|
||||
<BottomSheet
|
||||
onCancel = { this._onCancel }
|
||||
renderHeader = { this._renderMenuHeader }
|
||||
showSlidingView = { _isParticipantAvailable }>
|
||||
<Divider style = { styles.divider } />
|
||||
<SharedVideoButton { ...buttonProps } />
|
||||
</BottomSheet>
|
||||
);
|
||||
}
|
||||
|
||||
_onCancel: () => boolean;
|
||||
|
||||
/**
|
||||
* Callback to hide the {@code SharedVideoMenu}.
|
||||
*
|
||||
* @private
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_onCancel() {
|
||||
if (this.props._isOpen) {
|
||||
this.props.dispatch(hideSharedVideoMenu());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
_renderMenuHeader: () => React$Element<any>;
|
||||
|
||||
/**
|
||||
* Function to render the menu's header.
|
||||
*
|
||||
* @returns {React$Element}
|
||||
*/
|
||||
_renderMenuHeader() {
|
||||
const { _bottomSheetStyles, participant } = this.props;
|
||||
|
||||
return (
|
||||
<View
|
||||
style = { [
|
||||
_bottomSheetStyles.sheet,
|
||||
styles.participantNameContainer ] }>
|
||||
<Avatar
|
||||
participantId = { participant.id }
|
||||
size = { AVATAR_SIZE } />
|
||||
<Text style = { styles.participantNameLabel }>
|
||||
{ this.props._participantDisplayName }
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function that maps parts of Redux state tree into component props.
|
||||
*
|
||||
* @param {Object} state - Redux state.
|
||||
* @param {Object} ownProps - Properties of component.
|
||||
* @private
|
||||
* @returns {Props}
|
||||
*/
|
||||
function _mapStateToProps(state, ownProps) {
|
||||
const { participant } = ownProps;
|
||||
const isParticipantAvailable = getParticipantById(state, participant.id);
|
||||
|
||||
return {
|
||||
_bottomSheetStyles: ColorSchemeRegistry.get(state, 'BottomSheet'),
|
||||
_isOpen: isDialogOpen(state, SharedVideoMenu_),
|
||||
_isParticipantAvailable: Boolean(isParticipantAvailable),
|
||||
_participantDisplayName: getParticipantDisplayName(state, participant.id),
|
||||
_participantID: participant.id
|
||||
};
|
||||
}
|
||||
|
||||
SharedVideoMenu_ = connect(_mapStateToProps)(SharedVideoMenu);
|
||||
|
||||
export default SharedVideoMenu_;
|
|
@ -8,4 +8,5 @@ export { default as MuteEveryonesVideoDialog } from './MuteEveryonesVideoDialog'
|
|||
export { default as MuteRemoteParticipantDialog } from './MuteRemoteParticipantDialog';
|
||||
export { default as MuteRemoteParticipantsVideoDialog } from './MuteRemoteParticipantsVideoDialog';
|
||||
export { default as RemoteVideoMenu } from './RemoteVideoMenu';
|
||||
export { default as SharedVideoMenu } from './SharedVideoMenu';
|
||||
export { default as VolumeSlider } from './VolumeSlider';
|
||||
|
|
Loading…
Reference in New Issue