mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-01-09 08:10:18 +00:00
Compare commits
14 Commits
android-ap
...
7245
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f81446909c | ||
|
|
b4115593c0 | ||
|
|
f8bd8b616e | ||
|
|
be55ccd6f4 | ||
|
|
e7db18bd80 | ||
|
|
dff41e0fcb | ||
|
|
43be4324af | ||
|
|
c33baf4c96 | ||
|
|
f95a356025 | ||
|
|
1ba7765898 | ||
|
|
0346fca434 | ||
|
|
d267b2499d | ||
|
|
e3e5f1fbfa | ||
|
|
4697192b43 |
@@ -19,7 +19,7 @@ buildscript {
|
||||
ext {
|
||||
buildToolsVersion = "31.0.0"
|
||||
compileSdkVersion = 32
|
||||
minSdkVersion = 23
|
||||
minSdkVersion = 24
|
||||
targetSdkVersion = 32
|
||||
supportLibVersion = "28.0.0"
|
||||
|
||||
|
||||
@@ -74,10 +74,6 @@
|
||||
a:active {
|
||||
color: black;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-corner {
|
||||
background: #3a3a3a;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -98,7 +98,6 @@ platform :ios do
|
||||
demo_account_required: false,
|
||||
distribute_external: true,
|
||||
groups: ENV["JITSI_BETA_TESTING_GROUPS"],
|
||||
reject_build_waiting_for_review: true,
|
||||
uses_non_exempt_encryption: false
|
||||
)
|
||||
|
||||
|
||||
@@ -1150,6 +1150,7 @@
|
||||
"privateMessage": "Send private message",
|
||||
"profile": "Edit your profile",
|
||||
"raiseHand": "Raise your hand",
|
||||
"reactions": "Reactions",
|
||||
"reactionsMenu": "Reactions menu",
|
||||
"recording": "Toggle recording",
|
||||
"remoteMute": "Mute participant",
|
||||
@@ -1247,6 +1248,7 @@
|
||||
"reactionLike": "Send thumbs up reaction",
|
||||
"reactionSilence": "Send silence reaction",
|
||||
"reactionSurprised": "Send surprised reaction",
|
||||
"reactions": "Reactions",
|
||||
"security": "Security options",
|
||||
"selectBackground": "Select background",
|
||||
"shareRoom": "Invite someone",
|
||||
|
||||
27
package-lock.json
generated
27
package-lock.json
generated
@@ -60,7 +60,7 @@
|
||||
"js-md5": "0.6.1",
|
||||
"js-sha512": "0.8.0",
|
||||
"jwt-decode": "2.2.0",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1626.0.0+a41aa571/lib-jitsi-meet.tgz",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1629.0.0+1714bf07/lib-jitsi-meet.tgz",
|
||||
"lodash": "4.17.21",
|
||||
"moment": "2.29.4",
|
||||
"moment-duration-format": "2.2.2",
|
||||
@@ -12739,8 +12739,8 @@
|
||||
},
|
||||
"node_modules/lib-jitsi-meet": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1626.0.0+a41aa571/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-0LK5EMCvtsAEQkjkrWzyniTl8BKtshAZxI3e9hGnA/RVDuULLXre7GEWd2zCP3tCfdxclkhqt7skQpfNhCTdqg==",
|
||||
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1629.0.0+1714bf07/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-2gpi4oZwYFlNDOhsS3VC8eC0pR3vL6ekXBY+mFKe+gpRAh1d4S+st1r5kr7HGnkA/KF75+Ae5wDVq11knKJPRg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
@@ -17627,15 +17627,6 @@
|
||||
"ws": "^8.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/strophe.js/node_modules/@xmldom/xmldom": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.2.tgz",
|
||||
"integrity": "sha512-+R0juSseERyoPvnBQ/cZih6bpF7IpCXlWbHRoCRzYzqpz6gWHOgf8o4MOEf6KBVuOyqU+gCNLkCWVIJAro8XyQ==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/strophe.js/node_modules/ws": {
|
||||
"version": "8.13.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",
|
||||
@@ -29117,8 +29108,8 @@
|
||||
}
|
||||
},
|
||||
"lib-jitsi-meet": {
|
||||
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1626.0.0+a41aa571/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-0LK5EMCvtsAEQkjkrWzyniTl8BKtshAZxI3e9hGnA/RVDuULLXre7GEWd2zCP3tCfdxclkhqt7skQpfNhCTdqg==",
|
||||
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1629.0.0+1714bf07/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-2gpi4oZwYFlNDOhsS3VC8eC0pR3vL6ekXBY+mFKe+gpRAh1d4S+st1r5kr7HGnkA/KF75+Ae5wDVq11knKJPRg==",
|
||||
"requires": {
|
||||
"@jitsi/js-utils": "2.0.0",
|
||||
"@jitsi/logger": "2.0.0",
|
||||
@@ -32849,18 +32840,12 @@
|
||||
"resolved": "https://registry.npmjs.org/strophe.js/-/strophe.js-1.5.0.tgz",
|
||||
"integrity": "sha512-H5tE/tZxPR5xP3jhXyQwsjnMSwQMf7vrn9r1OkufTApyGHYe8WjzhsfxtL3AFhVu7vFjXPPZBrmUOTm1ccYgOA==",
|
||||
"requires": {
|
||||
"@xmldom/xmldom": "0.8.2",
|
||||
"@xmldom/xmldom": "0.8.7",
|
||||
"abab": "^2.0.3",
|
||||
"karma-rollup-preprocessor": "^7.0.8",
|
||||
"ws": "^8.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@xmldom/xmldom": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.2.tgz",
|
||||
"integrity": "sha512-+R0juSseERyoPvnBQ/cZih6bpF7IpCXlWbHRoCRzYzqpz6gWHOgf8o4MOEf6KBVuOyqU+gCNLkCWVIJAro8XyQ==",
|
||||
"optional": true
|
||||
},
|
||||
"ws": {
|
||||
"version": "8.13.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
"js-md5": "0.6.1",
|
||||
"js-sha512": "0.8.0",
|
||||
"jwt-decode": "2.2.0",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1626.0.0+a41aa571/lib-jitsi-meet.tgz",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1629.0.0+1714bf07/lib-jitsi-meet.tgz",
|
||||
"lodash": "4.17.21",
|
||||
"moment": "2.29.4",
|
||||
"moment-duration-format": "2.2.2",
|
||||
@@ -179,7 +179,7 @@
|
||||
"webpack-dev-server": "4.7.3"
|
||||
},
|
||||
"overrides": {
|
||||
"strophe.js@1.6.0": {
|
||||
"strophe.js@1.5.0": {
|
||||
"@xmldom/xmldom": "0.8.7"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -18,6 +18,7 @@ type ToolbarButtons = 'camera' |
|
||||
'participants-pane' |
|
||||
'profile' |
|
||||
'raisehand' |
|
||||
'reactions' |
|
||||
'recording' |
|
||||
'security' |
|
||||
'select-background' |
|
||||
|
||||
@@ -36,7 +36,7 @@ type Props = {
|
||||
/**
|
||||
* Function to render a bottom sheet footer element, if necessary.
|
||||
*/
|
||||
renderFooter?: Function;
|
||||
renderFooter?: () => React.ReactNode;
|
||||
|
||||
/**
|
||||
* Function to render a bottom sheet header element, if necessary.
|
||||
|
||||
@@ -18,7 +18,7 @@ interface IProps extends AbstractProps, WithTranslation {
|
||||
/**
|
||||
* The dialog descriptionKey.
|
||||
*/
|
||||
descriptionKey: string;
|
||||
descriptionKey?: string;
|
||||
|
||||
/**
|
||||
* An optional initial value to initiate the field with.
|
||||
|
||||
@@ -304,7 +304,8 @@ class Popover extends Component<IProps, IState> {
|
||||
&& !this.props.overflowDrawer
|
||||
&& this._contextMenuRef
|
||||
&& this._contextMenuRef.contains
|
||||
&& !this._contextMenuRef.contains(event.target as Node)) {
|
||||
&& !this._contextMenuRef.contains(event.target as Node)
|
||||
&& !this._containerRef?.current?.contains(event.target as Node)) {
|
||||
this._onHideDialog();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ export default class ToolboxItem extends AbstractToolboxItem<IProps> {
|
||||
* @returns {void}
|
||||
*/
|
||||
_onKeyPress(event?: React.KeyboardEvent) {
|
||||
if (event?.key === 'Enter' || event?.key === ' ') {
|
||||
if (event?.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
this.props.onClick();
|
||||
}
|
||||
|
||||
@@ -33,17 +33,17 @@ interface IProps {
|
||||
/**
|
||||
* Icon of the button.
|
||||
*/
|
||||
icon: Function;
|
||||
icon?: Function;
|
||||
|
||||
/**
|
||||
* Flag used for disabling the small icon.
|
||||
*/
|
||||
iconDisabled: boolean;
|
||||
iconDisabled?: boolean;
|
||||
|
||||
/**
|
||||
* The ID of the icon button.
|
||||
*/
|
||||
iconId: string;
|
||||
iconId?: string;
|
||||
|
||||
/**
|
||||
* Popover close callback.
|
||||
@@ -65,6 +65,11 @@ interface IProps {
|
||||
*/
|
||||
styles?: Object;
|
||||
|
||||
/**
|
||||
* Whether the trigger for open/ close should be click or hover.
|
||||
*/
|
||||
trigger?: 'hover' | 'click';
|
||||
|
||||
/**
|
||||
* Whether or not the popover is visible.
|
||||
*/
|
||||
@@ -77,7 +82,7 @@ interface IProps {
|
||||
* @param {Object} props - Component's props.
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
export default function ToolboxButtonWithIconPopup(props: IProps) {
|
||||
export default function ToolboxButtonWithPopup(props: IProps) {
|
||||
const {
|
||||
ariaControls,
|
||||
ariaExpanded,
|
||||
@@ -91,9 +96,29 @@ export default function ToolboxButtonWithIconPopup(props: IProps) {
|
||||
onPopoverOpen,
|
||||
popoverContent,
|
||||
styles,
|
||||
trigger,
|
||||
visible
|
||||
} = props;
|
||||
|
||||
if (!icon) {
|
||||
return (
|
||||
<div
|
||||
className = 'settings-button-container'
|
||||
style = { styles }>
|
||||
<Popover
|
||||
content = { popoverContent }
|
||||
headingLabel = { ariaLabel }
|
||||
onPopoverClose = { onPopoverClose }
|
||||
onPopoverOpen = { onPopoverOpen }
|
||||
position = 'top'
|
||||
trigger = { trigger }
|
||||
visible = { visible }>
|
||||
{children}
|
||||
</Popover>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const iconProps: any = {};
|
||||
|
||||
if (iconDisabled) {
|
||||
@@ -70,7 +70,7 @@ const useStyles = makeStyles()(theme => {
|
||||
'& input[type="checkbox"]': {
|
||||
appearance: 'none',
|
||||
backgroundColor: 'transparent',
|
||||
margin: 0,
|
||||
margin: '3px',
|
||||
font: 'inherit',
|
||||
color: theme.palette.icon03,
|
||||
width: '18px',
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/* eslint-disable react/no-multi-comp */
|
||||
|
||||
import { useIsFocused } from '@react-navigation/native';
|
||||
import { Route, useIsFocused } from '@react-navigation/native';
|
||||
import React, { useEffect } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
@@ -9,7 +8,7 @@ import JitsiScreen from '../../../base/modal/components/JitsiScreen';
|
||||
import { TabBarLabelCounter } from '../../../mobile/navigation/components/TabBarLabelCounter';
|
||||
import { closeChat } from '../../actions.native';
|
||||
import AbstractChat, {
|
||||
type Props as AbstractProps,
|
||||
IProps as AbstractProps,
|
||||
_mapStateToProps
|
||||
} from '../AbstractChat';
|
||||
|
||||
@@ -18,24 +17,24 @@ import MessageContainer from './MessageContainer';
|
||||
import MessageRecipient from './MessageRecipient';
|
||||
import styles from './styles';
|
||||
|
||||
type Props = AbstractProps & {
|
||||
interface IProps extends AbstractProps {
|
||||
|
||||
/**
|
||||
* Default prop for navigating between screen components(React Navigation).
|
||||
*/
|
||||
navigation: Object,
|
||||
navigation: any;
|
||||
|
||||
/**
|
||||
* Default prop for navigating between screen components(React Navigation).
|
||||
*/
|
||||
route: Object
|
||||
};
|
||||
route: Route<'', { privateMessageRecipient: { name: string; }; }>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements a React native component that renders the chat window (modal) of
|
||||
* the mobile client.
|
||||
*/
|
||||
class Chat extends AbstractChat<Props> {
|
||||
class Chat extends AbstractChat<IProps> {
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
@@ -51,17 +50,16 @@ class Chat extends AbstractChat<Props> {
|
||||
hasBottomTextInput = { true }
|
||||
hasTabNavigator = { true }
|
||||
style = { styles.chatContainer }>
|
||||
{/* @ts-ignore */}
|
||||
<MessageContainer messages = { _messages } />
|
||||
<MessageRecipient privateMessageRecipient = { privateMessageRecipient } />
|
||||
<ChatInputBar onSend = { this._onSendMessage } />
|
||||
</JitsiScreen>
|
||||
);
|
||||
}
|
||||
|
||||
_onSendMessage: (string) => void;
|
||||
}
|
||||
|
||||
export default translate(connect(_mapStateToProps)(props => {
|
||||
export default translate(connect(_mapStateToProps)((props: IProps) => {
|
||||
const { _nbUnreadMessages, dispatch, navigation, t } = props;
|
||||
const unreadMessagesNr = _nbUnreadMessages > 0;
|
||||
|
||||
@@ -78,7 +76,9 @@ export default translate(connect(_mapStateToProps)(props => {
|
||||
)
|
||||
});
|
||||
|
||||
return () => isFocused && dispatch(closeChat());
|
||||
return () => {
|
||||
isFocused && dispatch(closeChat());
|
||||
};
|
||||
}, [ isFocused, _nbUnreadMessages ]);
|
||||
|
||||
return (
|
||||
@@ -1,5 +1,6 @@
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { CHAT_ENABLED } from '../../../base/flags/constants';
|
||||
import { getFeatureFlag } from '../../../base/flags/functions';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
@@ -10,23 +11,23 @@ import { screen } from '../../../mobile/navigation/routes';
|
||||
import { getUnreadPollCount } from '../../../polls/functions';
|
||||
import { getUnreadCount } from '../../functions';
|
||||
|
||||
type Props = AbstractButtonProps & {
|
||||
interface IProps extends AbstractButtonProps {
|
||||
|
||||
/**
|
||||
* True if the polls feature is disabled.
|
||||
*/
|
||||
_isPollsDisabled: boolean,
|
||||
_isPollsDisabled?: boolean;
|
||||
|
||||
/**
|
||||
* The unread message count.
|
||||
*/
|
||||
_unreadMessageCount: number
|
||||
};
|
||||
_unreadMessageCount: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements an {@link AbstractButton} to open the chat screen on mobile.
|
||||
*/
|
||||
class ChatButton extends AbstractButton<Props, *> {
|
||||
class ChatButton extends AbstractButton<IProps> {
|
||||
accessibilityLabel = 'toolbar.accessibilityLabel.chat';
|
||||
icon = IconMessage;
|
||||
label = 'toolbar.chat';
|
||||
@@ -60,9 +61,9 @@ class ChatButton extends AbstractButton<Props, *> {
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @param {Object} ownProps - The properties explicitly passed to the component instance.
|
||||
* @returns {Props}
|
||||
* @returns {IProps}
|
||||
*/
|
||||
function _mapStateToProps(state, ownProps) {
|
||||
function _mapStateToProps(state: IReduxState, ownProps: any) {
|
||||
const enabled = getFeatureFlag(state, CHAT_ENABLED, true);
|
||||
const { disablePolls } = state['features/base/config'];
|
||||
const { visible = enabled } = ownProps;
|
||||
@@ -25,10 +25,6 @@ class ChatPrivacyDialog extends AbstractChatPrivacyDialog {
|
||||
onSubmit = { this._onSendPrivateMessage } />
|
||||
);
|
||||
}
|
||||
|
||||
_onSendGroupMessage: () => boolean;
|
||||
|
||||
_onSendPrivateMessage: () => boolean;
|
||||
}
|
||||
|
||||
export default translate(connect(_mapStateToProps, _mapDispatchToProps)(ChatPrivacyDialog));
|
||||
@@ -1,63 +1,65 @@
|
||||
import React from 'react';
|
||||
import { Text, TouchableHighlight, View } from 'react-native';
|
||||
import { Text, TouchableHighlight, View, ViewStyle } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState, IStore } from '../../../app/types';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
import Icon from '../../../base/icons/components/Icon';
|
||||
import { IconCloseLarge } from '../../../base/icons/svg';
|
||||
import { ILocalParticipant } from '../../../base/participants/types';
|
||||
import {
|
||||
setParams
|
||||
} from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
|
||||
import { setLobbyChatActiveState, setPrivateMessageRecipient } from '../../actions.any';
|
||||
import AbstractMessageRecipient, {
|
||||
type Props as AbstractProps
|
||||
IProps as AbstractProps
|
||||
} from '../AbstractMessageRecipient';
|
||||
|
||||
import styles from './styles';
|
||||
|
||||
|
||||
type Props = AbstractProps & {
|
||||
interface IProps extends AbstractProps {
|
||||
|
||||
/**
|
||||
* The Redux dispatch function.
|
||||
*/
|
||||
dispatch: Function,
|
||||
dispatch: IStore['dispatch'];
|
||||
|
||||
/**
|
||||
* Is lobby messaging active.
|
||||
*/
|
||||
isLobbyChatActive: boolean,
|
||||
isLobbyChatActive: boolean;
|
||||
|
||||
/**
|
||||
* The participant string for lobby chat messaging.
|
||||
*/
|
||||
lobbyMessageRecipient: Object,
|
||||
lobbyMessageRecipient?: {
|
||||
id: string;
|
||||
name: string;
|
||||
} | ILocalParticipant;
|
||||
|
||||
/**
|
||||
* The participant object set for private messaging.
|
||||
*/
|
||||
privateMessageRecipient: Object,
|
||||
};
|
||||
privateMessageRecipient: { name: string; };
|
||||
}
|
||||
|
||||
/**
|
||||
* Class to implement the displaying of the recipient of the next message.
|
||||
*/
|
||||
class MessageRecipient extends AbstractMessageRecipient<Props> {
|
||||
class MessageRecipient extends AbstractMessageRecipient<IProps> {
|
||||
|
||||
/**
|
||||
* Constructor of the component.
|
||||
*
|
||||
* @param {Props} props - The props of the component.
|
||||
* @param {IProps} props - The props of the component.
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this._onResetPrivateMessageRecipient = this._onResetPrivateMessageRecipient.bind(this);
|
||||
this._onResetLobbyMessageRecipient = this._onResetLobbyMessageRecipient.bind(this);
|
||||
}
|
||||
|
||||
_onResetLobbyMessageRecipient: () => void;
|
||||
|
||||
/**
|
||||
* Resets lobby message recipient from state.
|
||||
*
|
||||
@@ -69,8 +71,6 @@ class MessageRecipient extends AbstractMessageRecipient<Props> {
|
||||
dispatch(setLobbyChatActiveState(false));
|
||||
}
|
||||
|
||||
_onResetPrivateMessageRecipient: () => void;
|
||||
|
||||
/**
|
||||
* Resets private message recipient from state.
|
||||
*
|
||||
@@ -102,10 +102,10 @@ class MessageRecipient extends AbstractMessageRecipient<Props> {
|
||||
|
||||
if (isLobbyChatActive) {
|
||||
return (
|
||||
<View style = { styles.lobbyMessageRecipientContainer }>
|
||||
<View style = { styles.lobbyMessageRecipientContainer as ViewStyle }>
|
||||
<Text style = { styles.messageRecipientText }>
|
||||
{ t('chat.lobbyChatMessageTo', {
|
||||
recipient: lobbyMessageRecipient.name
|
||||
recipient: lobbyMessageRecipient?.name
|
||||
}) }
|
||||
</Text>
|
||||
<TouchableHighlight
|
||||
@@ -123,7 +123,7 @@ class MessageRecipient extends AbstractMessageRecipient<Props> {
|
||||
}
|
||||
|
||||
return (
|
||||
<View style = { styles.messageRecipientContainer }>
|
||||
<View style = { styles.messageRecipientContainer as ViewStyle }>
|
||||
<Text style = { styles.messageRecipientText }>
|
||||
{ t('chat.messageTo', {
|
||||
recipient: privateMessageRecipient.name
|
||||
@@ -145,9 +145,10 @@ class MessageRecipient extends AbstractMessageRecipient<Props> {
|
||||
* Maps part of the redux state to the props of this component.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @returns {Props}
|
||||
* @param {any} _ownProps - Component's own props.
|
||||
* @returns {IProps}
|
||||
*/
|
||||
function _mapStateToProps(state) {
|
||||
function _mapStateToProps(state: IReduxState, _ownProps: any) {
|
||||
const { lobbyMessageRecipient, isLobbyChatActive } = state['features/chat'];
|
||||
|
||||
return {
|
||||
@@ -29,11 +29,11 @@ const styles = (theme: Theme) => {
|
||||
chatMessage: {
|
||||
display: 'inline-flex',
|
||||
padding: '12px',
|
||||
marginRight: '12px',
|
||||
backgroundColor: theme.palette.ui02,
|
||||
borderRadius: '4px 12px 12px 12px',
|
||||
maxWidth: '100%',
|
||||
marginTop: '4px',
|
||||
boxSizing: 'border-box' as const,
|
||||
|
||||
'&.privatemessage': {
|
||||
backgroundColor: theme.palette.support05
|
||||
@@ -62,7 +62,8 @@ const styles = (theme: Theme) => {
|
||||
replyWrapper: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row' as const,
|
||||
alignItems: 'center'
|
||||
alignItems: 'center',
|
||||
maxWidth: '100%'
|
||||
},
|
||||
|
||||
messageContent: {
|
||||
@@ -126,7 +127,7 @@ class ChatMessage extends AbstractChatMessage<IProps> {
|
||||
|
||||
return (
|
||||
<div
|
||||
className = { classes.chatMessageWrapper }
|
||||
className = { clsx(classes.chatMessageWrapper, type) }
|
||||
id = { this.props.message.messageId }
|
||||
tabIndex = { -1 }>
|
||||
<div
|
||||
|
||||
@@ -25,7 +25,11 @@ const useStyles = makeStyles()(theme => {
|
||||
messageGroup: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
maxWidth: '100%'
|
||||
maxWidth: '100%',
|
||||
|
||||
'&.remote': {
|
||||
maxWidth: 'calc(100% - 40px)' // 100% - avatar and margin
|
||||
}
|
||||
},
|
||||
|
||||
groupContainer: {
|
||||
|
||||
@@ -28,6 +28,13 @@ const useStyles = makeStyles()(theme => {
|
||||
color: theme.palette.text01
|
||||
},
|
||||
|
||||
text: {
|
||||
maxWidth: 'calc(100% - 30px)',
|
||||
overflow: 'hidden',
|
||||
whiteSpace: 'break-spaces',
|
||||
wordBreak: 'break-all'
|
||||
},
|
||||
|
||||
iconButton: {
|
||||
padding: '2px',
|
||||
|
||||
@@ -72,7 +79,7 @@ const MessageRecipient = ({
|
||||
className = { classes.container }
|
||||
id = 'chat-recipient'
|
||||
role = 'alert'>
|
||||
<span>
|
||||
<span className = { classes.text }>
|
||||
{t(_isLobbyChatActive ? 'chat.lobbyChatMessageTo' : 'chat.messageTo', {
|
||||
recipient: _isLobbyChatActive ? _lobbyMessageRecipient : _privateMessageRecipient
|
||||
})}
|
||||
|
||||
@@ -27,15 +27,6 @@ export const HIDE_GIF_FOR_PARTICIPANT = 'HIDE_GIF_FOR_PARTICIPANT';
|
||||
*/
|
||||
export const REMOVE_GIF_FOR_PARTICIPANT = 'REMOVE_GIF_FOR_PARTICIPANT';
|
||||
|
||||
/**
|
||||
* Set gif menu drawer visibility.
|
||||
* {{
|
||||
* type: SET_GIF_DRAWER_VISIBILITY,
|
||||
* visible: boolean
|
||||
* }}
|
||||
*/
|
||||
export const SET_GIF_DRAWER_VISIBILITY = 'SET_GIF_DRAWER_VISIBILITY';
|
||||
|
||||
/**
|
||||
* Set gif menu visibility.
|
||||
* {{
|
||||
|
||||
@@ -2,7 +2,6 @@ import {
|
||||
ADD_GIF_FOR_PARTICIPANT,
|
||||
HIDE_GIF_FOR_PARTICIPANT,
|
||||
REMOVE_GIF_FOR_PARTICIPANT,
|
||||
SET_GIF_DRAWER_VISIBILITY,
|
||||
SET_GIF_MENU_VISIBILITY,
|
||||
SHOW_GIF_FOR_PARTICIPANT
|
||||
} from './actionTypes';
|
||||
@@ -61,19 +60,6 @@ export function hideGif(participantId: string) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set visibility of the GIF drawer.
|
||||
*
|
||||
* @param {boolean} visible - Whether or not it should be visible.
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function setGifDrawerVisibility(visible: boolean) {
|
||||
return {
|
||||
type: SET_GIF_DRAWER_VISIBILITY,
|
||||
visible
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set visibility of the GIF menu.
|
||||
*
|
||||
|
||||
@@ -12,11 +12,10 @@ import Input from '../../../base/ui/components/web/Input';
|
||||
import { sendMessage } from '../../../chat/actions.any';
|
||||
import { SCROLL_SIZE } from '../../../filmstrip/constants';
|
||||
import { toggleReactionsMenuVisibility } from '../../../reactions/actions.web';
|
||||
import { setOverflowMenuVisible } from '../../../toolbox/actions.web';
|
||||
import { IReactionsMenuParent } from '../../../reactions/types';
|
||||
import Drawer from '../../../toolbox/components/web/Drawer';
|
||||
import JitsiPortal from '../../../toolbox/components/web/JitsiPortal';
|
||||
import { showOverflowDrawer } from '../../../toolbox/functions.web';
|
||||
import { setGifDrawerVisibility } from '../../actions';
|
||||
import { setGifMenuVisibility } from '../../actions';
|
||||
import {
|
||||
formatGifUrlMessage,
|
||||
getGifAPIKey,
|
||||
@@ -60,10 +59,17 @@ const useStyles = makeStyles()(theme => {
|
||||
marginTop: theme.spacing(1)
|
||||
},
|
||||
|
||||
overflowMenu: {
|
||||
overflowDrawerMenu: {
|
||||
padding: theme.spacing(3),
|
||||
width: '100%',
|
||||
boxSizing: 'border-box'
|
||||
boxSizing: 'border-box',
|
||||
height: '100%'
|
||||
},
|
||||
|
||||
overflowMenu: {
|
||||
height: '200px',
|
||||
width: '201px',
|
||||
marginBottom: '0px'
|
||||
},
|
||||
|
||||
gifContainerOverflow: {
|
||||
@@ -77,19 +83,25 @@ const useStyles = makeStyles()(theme => {
|
||||
};
|
||||
});
|
||||
|
||||
interface IProps {
|
||||
columns?: number;
|
||||
parent: IReactionsMenuParent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gifs menu.
|
||||
*
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
function GifsMenu() {
|
||||
function GifsMenu({ columns = 2, parent }: IProps) {
|
||||
const API_KEY = useSelector(getGifAPIKey);
|
||||
const giphyFetch = new GiphyFetch(API_KEY);
|
||||
const [ searchKey, setSearchKey ] = useState<string>();
|
||||
const { classes: styles, cx } = useStyles();
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
const overflowDrawer: boolean = useSelector(showOverflowDrawer);
|
||||
const isInOverflowMenu
|
||||
= parent === IReactionsMenuParent.OverflowDrawer || parent === IReactionsMenuParent.OverflowMenu;
|
||||
const { clientWidth } = useSelector((state: IReduxState) => state['features/base/responsive-ui']);
|
||||
const rating = useSelector(getGifRating);
|
||||
const proxyUrl = useSelector(getGiphyProxyUrl);
|
||||
@@ -109,8 +121,7 @@ function GifsMenu() {
|
||||
}, [ searchKey ]);
|
||||
|
||||
const onDrawerClose = useCallback(() => {
|
||||
dispatch(setGifDrawerVisibility(false));
|
||||
dispatch(setOverflowMenuVisible(false));
|
||||
dispatch(setGifMenuVisibility(false));
|
||||
}, []);
|
||||
|
||||
const handleGifClick = useCallback((gif, e) => {
|
||||
@@ -121,9 +132,9 @@ function GifsMenu() {
|
||||
batch(() => {
|
||||
dispatch(sendMessage(formatGifUrlMessage(url), true));
|
||||
dispatch(toggleReactionsMenuVisibility());
|
||||
overflowDrawer && onDrawerClose();
|
||||
isInOverflowMenu && onDrawerClose();
|
||||
});
|
||||
}, [ dispatch, overflowDrawer ]);
|
||||
}, [ dispatch, isInOverflowMenu ]);
|
||||
|
||||
const handleGifKeyPress = useCallback((gif, e) => {
|
||||
if (e.nativeEvent.keyCode === 13) {
|
||||
@@ -191,7 +202,8 @@ function GifsMenu() {
|
||||
const gifMenu = (
|
||||
<div
|
||||
className = { cx(styles.gifsMenu,
|
||||
overflowDrawer && styles.overflowMenu
|
||||
parent === IReactionsMenuParent.OverflowDrawer && styles.overflowDrawerMenu,
|
||||
parent === IReactionsMenuParent.OverflowMenu && styles.overflowMenu
|
||||
) }>
|
||||
<Input
|
||||
autoFocus = { true }
|
||||
@@ -208,9 +220,9 @@ function GifsMenu() {
|
||||
value = { searchKey ?? '' } />
|
||||
<div
|
||||
className = { cx(styles.gifContainer,
|
||||
overflowDrawer && styles.gifContainerOverflow) }>
|
||||
parent === IReactionsMenuParent.OverflowDrawer && styles.gifContainerOverflow) }>
|
||||
<Grid
|
||||
columns = { 2 }
|
||||
columns = { columns }
|
||||
fetchGifs = { fetchGifs }
|
||||
gutter = { 6 }
|
||||
hideAttribution = { true }
|
||||
@@ -219,9 +231,9 @@ function GifsMenu() {
|
||||
noResultsMessage = { t('giphy.noResults') }
|
||||
onGifClick = { handleGifClick }
|
||||
onGifKeyPress = { handleGifKeyPress }
|
||||
width = { overflowDrawer
|
||||
width = { parent === IReactionsMenuParent.OverflowDrawer
|
||||
? clientWidth - (2 * OVERFLOW_DRAWER_PADDING) - SCROLL_SIZE
|
||||
: 320
|
||||
: parent === IReactionsMenuParent.OverflowMenu ? 201 : 320
|
||||
} />
|
||||
</div>
|
||||
<div className = { styles.logoContainer }>
|
||||
@@ -233,7 +245,7 @@ function GifsMenu() {
|
||||
</div>
|
||||
);
|
||||
|
||||
return overflowDrawer ? (
|
||||
return parent === IReactionsMenuParent.OverflowDrawer ? (
|
||||
<JitsiPortal>
|
||||
<Drawer
|
||||
className = { styles.drawer }
|
||||
|
||||
@@ -3,30 +3,29 @@ import { useTranslation } from 'react-i18next';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import ReactionButton from '../../../reactions/components/web/ReactionButton';
|
||||
import { showOverflowDrawer } from '../../../toolbox/functions.web';
|
||||
import { setGifDrawerVisibility, setGifMenuVisibility } from '../../actions';
|
||||
import { IReactionsMenuParent } from '../../../reactions/types';
|
||||
import { setGifMenuVisibility } from '../../actions';
|
||||
import { isGifsMenuOpen } from '../../functions.web';
|
||||
|
||||
const GifsMenuButton = () => {
|
||||
interface IProps {
|
||||
parent: IReactionsMenuParent;
|
||||
}
|
||||
|
||||
const GifsMenuButton = ({ parent }: IProps) => {
|
||||
const menuOpen = useSelector(isGifsMenuOpen);
|
||||
const overflowDrawer = useSelector(showOverflowDrawer);
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const icon = (
|
||||
<img
|
||||
alt = 'GIPHY Logo'
|
||||
height = { 24 }
|
||||
height = { parent === IReactionsMenuParent.OverflowMenu ? 16 : 24 }
|
||||
src = 'images/GIPHY_icon.png' />
|
||||
);
|
||||
|
||||
const handleClick = useCallback(() =>
|
||||
dispatch(
|
||||
overflowDrawer
|
||||
? setGifDrawerVisibility(!menuOpen)
|
||||
: setGifMenuVisibility(!menuOpen)
|
||||
)
|
||||
, [ menuOpen, overflowDrawer ]);
|
||||
const handleClick = useCallback(() => {
|
||||
dispatch(setGifMenuVisibility(!menuOpen));
|
||||
}, [ menuOpen, parent ]);
|
||||
|
||||
return (
|
||||
<ReactionButton
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { IReduxState } from '../app/types';
|
||||
import { showOverflowDrawer } from '../toolbox/functions.web';
|
||||
|
||||
export * from './function.any';
|
||||
|
||||
@@ -10,8 +9,5 @@ export * from './function.any';
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isGifsMenuOpen(state: IReduxState) {
|
||||
const overflowDrawer = showOverflowDrawer(state);
|
||||
const { drawerVisible, menuOpen } = state['features/gifs'];
|
||||
|
||||
return overflowDrawer ? drawerVisible : menuOpen;
|
||||
return state['features/gifs'].menuOpen;
|
||||
}
|
||||
|
||||
@@ -4,12 +4,10 @@ import {
|
||||
ADD_GIF_FOR_PARTICIPANT,
|
||||
HIDE_GIF_FOR_PARTICIPANT,
|
||||
REMOVE_GIF_FOR_PARTICIPANT,
|
||||
SET_GIF_DRAWER_VISIBILITY,
|
||||
SET_GIF_MENU_VISIBILITY
|
||||
} from './actionTypes';
|
||||
|
||||
const initialState = {
|
||||
drawerVisible: false,
|
||||
gifList: new Map(),
|
||||
menuOpen: false
|
||||
};
|
||||
@@ -20,7 +18,6 @@ export interface IGif {
|
||||
}
|
||||
|
||||
export interface IGifsState {
|
||||
drawerVisible: boolean;
|
||||
gifList: Map<string, IGif>;
|
||||
menuOpen: boolean;
|
||||
}
|
||||
@@ -66,11 +63,6 @@ ReducerRegistry.register<IGifsState>(
|
||||
gifList: newList
|
||||
};
|
||||
}
|
||||
case SET_GIF_DRAWER_VISIBILITY:
|
||||
return {
|
||||
...state,
|
||||
drawerVisible: action.visible
|
||||
};
|
||||
case SET_GIF_MENU_VISIBILITY:
|
||||
return {
|
||||
...state,
|
||||
|
||||
@@ -97,7 +97,7 @@ class DialInNumber extends Component<IProps> {
|
||||
{ phoneNumber }
|
||||
</span>
|
||||
</span>
|
||||
<span className = 'spacer'> </span>
|
||||
<br />
|
||||
<span className = 'conference-id'>
|
||||
<span className = 'info-label'>
|
||||
{ t('info.dialInConferenceID') }
|
||||
|
||||
@@ -101,9 +101,12 @@ class ReactionButton extends AbstractToolbarButton<IProps, IState> {
|
||||
/**
|
||||
* Handles reaction button click.
|
||||
*
|
||||
* @param {Event} event - The click event.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onClickHandler() {
|
||||
_onClickHandler(event: any) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.props.onClick();
|
||||
clearTimeout(this.state.increaseTimeout ?? 0);
|
||||
const timeout = window.setTimeout(() => {
|
||||
|
||||
@@ -6,7 +6,6 @@ import { makeStyles } from 'tss-react/mui';
|
||||
import { createReactionMenuEvent, createToolbarEvent } from '../../../analytics/AnalyticsEvents';
|
||||
import { sendAnalytics } from '../../../analytics/functions';
|
||||
import { IReduxState, IStore } from '../../../app/types';
|
||||
import { isMobileBrowser } from '../../../base/environment/utils';
|
||||
import { raiseHand } from '../../../base/participants/actions';
|
||||
import { getLocalParticipant, hasRaisedHand } from '../../../base/participants/functions';
|
||||
import GifsMenu from '../../../gifs/components/web/GifsMenu';
|
||||
@@ -15,7 +14,13 @@ import { isGifEnabled, isGifsMenuOpen } from '../../../gifs/functions';
|
||||
import { dockToolbox } from '../../../toolbox/actions.web';
|
||||
import { addReactionToBuffer } from '../../actions.any';
|
||||
import { toggleReactionsMenuVisibility } from '../../actions.web';
|
||||
import { REACTIONS, REACTIONS_MENU_HEIGHT } from '../../constants';
|
||||
import {
|
||||
GIFS_MENU_HEIGHT_IN_OVERFLOW_MENU,
|
||||
RAISE_HAND_ROW_HEIGHT, REACTIONS,
|
||||
REACTIONS_MENU_HEIGHT_DRAWER,
|
||||
REACTIONS_MENU_HEIGHT_IN_OVERFLOW_MENU
|
||||
} from '../../constants';
|
||||
import { IReactionsMenuParent } from '../../types';
|
||||
|
||||
import ReactionButton from './ReactionButton';
|
||||
|
||||
@@ -36,11 +41,6 @@ interface IProps {
|
||||
*/
|
||||
_isGifMenuVisible: boolean;
|
||||
|
||||
/**
|
||||
* Whether or not it's a mobile browser.
|
||||
*/
|
||||
_isMobile: boolean;
|
||||
|
||||
/**
|
||||
* The ID of the local participant.
|
||||
*/
|
||||
@@ -57,13 +57,59 @@ interface IProps {
|
||||
dispatch: IStore['dispatch'];
|
||||
|
||||
/**
|
||||
* Whether or not it's displayed in the overflow menu.
|
||||
* Indicates the parent of the reactions menu.
|
||||
*/
|
||||
overflowMenu?: boolean;
|
||||
parent: IReactionsMenuParent;
|
||||
|
||||
/**
|
||||
* Whether to show the raised hand button.
|
||||
*/
|
||||
showRaisedHand?: boolean;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles()(theme => {
|
||||
const useStyles = makeStyles<IProps>()((theme, props: IProps) => {
|
||||
const { parent, showRaisedHand, _isGifMenuVisible } = props;
|
||||
let reactionsMenuHeight = REACTIONS_MENU_HEIGHT_DRAWER;
|
||||
|
||||
if (parent === IReactionsMenuParent.OverflowDrawer || parent === IReactionsMenuParent.OverflowMenu) {
|
||||
if (parent === IReactionsMenuParent.OverflowMenu) {
|
||||
reactionsMenuHeight = REACTIONS_MENU_HEIGHT_IN_OVERFLOW_MENU;
|
||||
|
||||
if (_isGifMenuVisible) {
|
||||
reactionsMenuHeight += GIFS_MENU_HEIGHT_IN_OVERFLOW_MENU;
|
||||
}
|
||||
}
|
||||
if (!showRaisedHand) {
|
||||
reactionsMenuHeight -= RAISE_HAND_ROW_HEIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
reactionsMenuInOverflowMenu: {
|
||||
'&.reactions-menu': {
|
||||
'&.with-gif': {
|
||||
width: 'inherit'
|
||||
},
|
||||
'.reactions-row': {
|
||||
'.toolbox-icon': {
|
||||
width: '24px',
|
||||
height: '24px',
|
||||
|
||||
'span.emoji': {
|
||||
width: '24px',
|
||||
height: '24px',
|
||||
lineHeight: '24px',
|
||||
fontSize: '16px'
|
||||
}
|
||||
}
|
||||
},
|
||||
'.raise-hand-row': {
|
||||
'.toolbox-icon': {
|
||||
height: '32px'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
overflow: {
|
||||
width: 'auto',
|
||||
paddingBottom: 'max(env(safe-area-inset-bottom, 0), 16px)',
|
||||
@@ -72,21 +118,55 @@ const useStyles = makeStyles()(theme => {
|
||||
borderRadius: 0,
|
||||
position: 'relative',
|
||||
boxSizing: 'border-box',
|
||||
height: `${REACTIONS_MENU_HEIGHT}px`
|
||||
height: `${reactionsMenuHeight}px`
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const ReactionsMenu = ({
|
||||
_dockToolbox,
|
||||
_isGifEnabled,
|
||||
_isGifMenuVisible,
|
||||
_isMobile,
|
||||
_raisedHand,
|
||||
dispatch,
|
||||
overflowMenu
|
||||
}: IProps) => {
|
||||
const { classes, cx } = useStyles();
|
||||
const _getReactionButtons = (dispatch: IStore['dispatch'], t: Function) => {
|
||||
let modifierKey = 'Alt';
|
||||
|
||||
if (window.navigator?.platform) {
|
||||
if (window.navigator.platform.indexOf('Mac') !== -1) {
|
||||
modifierKey = '⌥';
|
||||
}
|
||||
}
|
||||
|
||||
return Object.keys(REACTIONS).map(key => {
|
||||
/**
|
||||
* Sends reaction message.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
function doSendReaction() {
|
||||
dispatch(addReactionToBuffer(key));
|
||||
sendAnalytics(createReactionMenuEvent(key));
|
||||
}
|
||||
|
||||
return (<ReactionButton
|
||||
accessibilityLabel = { t(`toolbar.accessibilityLabel.${key}`) }
|
||||
icon = { REACTIONS[key].emoji }
|
||||
key = { key }
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick = { doSendReaction }
|
||||
toggled = { false }
|
||||
tooltip = { `${t(`toolbar.${key}`)} (${modifierKey} + ${REACTIONS[key].shortcutChar})` } />);
|
||||
});
|
||||
};
|
||||
|
||||
const ReactionsMenu = (props: IProps) => {
|
||||
const {
|
||||
_dockToolbox,
|
||||
_isGifEnabled,
|
||||
_isGifMenuVisible,
|
||||
_raisedHand,
|
||||
dispatch,
|
||||
parent,
|
||||
showRaisedHand = false
|
||||
} = props;
|
||||
const isInOverflowMenu
|
||||
= parent === IReactionsMenuParent.OverflowDrawer || parent === IReactionsMenuParent.OverflowMenu;
|
||||
const { classes, cx } = useStyles(props);
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
@@ -97,9 +177,9 @@ const ReactionsMenu = ({
|
||||
};
|
||||
}, []);
|
||||
|
||||
const _doToggleRaiseHand = () => {
|
||||
const _doToggleRaiseHand = useCallback(() => {
|
||||
dispatch(raiseHand(!_raisedHand));
|
||||
};
|
||||
}, [ _raisedHand ]);
|
||||
|
||||
const _onToolbarToggleRaiseHand = useCallback(() => {
|
||||
sendAnalytics(createToolbarEvent(
|
||||
@@ -109,47 +189,26 @@ const ReactionsMenu = ({
|
||||
dispatch(toggleReactionsMenuVisibility());
|
||||
}, [ _raisedHand ]);
|
||||
|
||||
const _getReactionButtons = () => {
|
||||
let modifierKey = 'Alt';
|
||||
const buttons = _getReactionButtons(dispatch, t);
|
||||
|
||||
if (window.navigator?.platform) {
|
||||
if (window.navigator.platform.indexOf('Mac') !== -1) {
|
||||
modifierKey = '⌥';
|
||||
}
|
||||
}
|
||||
|
||||
return Object.keys(REACTIONS).map(key => {
|
||||
/**
|
||||
* Sends reaction message.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
function doSendReaction() {
|
||||
dispatch(addReactionToBuffer(key));
|
||||
sendAnalytics(createReactionMenuEvent(key));
|
||||
}
|
||||
|
||||
return (<ReactionButton
|
||||
accessibilityLabel = { t(`toolbar.accessibilityLabel.${key}`) }
|
||||
icon = { REACTIONS[key].emoji }
|
||||
key = { key }
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick = { doSendReaction }
|
||||
toggled = { false }
|
||||
tooltip = { `${t(`toolbar.${key}`)} (${modifierKey} + ${REACTIONS[key].shortcutChar})` } />);
|
||||
});
|
||||
};
|
||||
if (_isGifEnabled) {
|
||||
buttons.push(<GifsMenuButton parent = { parent } />);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className = { cx('reactions-menu', _isGifEnabled && 'with-gif',
|
||||
overflowMenu && `overflow ${classes.overflow}`) }>
|
||||
{_isGifEnabled && _isGifMenuVisible && <GifsMenu />}
|
||||
className = { cx('reactions-menu',
|
||||
parent === IReactionsMenuParent.OverflowMenu && classes.reactionsMenuInOverflowMenu,
|
||||
_isGifEnabled && 'with-gif',
|
||||
isInOverflowMenu && `overflow ${classes.overflow}`) }>
|
||||
{_isGifEnabled && _isGifMenuVisible
|
||||
&& <GifsMenu
|
||||
columns = { parent === IReactionsMenuParent.OverflowMenu ? 1 : undefined }
|
||||
parent = { parent } />}
|
||||
<div className = 'reactions-row'>
|
||||
{_getReactionButtons()}
|
||||
{_isGifEnabled && <GifsMenuButton />}
|
||||
{ buttons }
|
||||
</div>
|
||||
{_isMobile && (
|
||||
{showRaisedHand && (
|
||||
<div className = 'raise-hand-row'>
|
||||
<ReactionButton
|
||||
accessibilityLabel = { t('toolbar.accessibilityLabel.raiseHand') }
|
||||
@@ -157,7 +216,7 @@ const ReactionsMenu = ({
|
||||
key = 'raisehand'
|
||||
label = {
|
||||
`${t(`toolbar.${_raisedHand ? 'lowerYourHand' : 'raiseYourHand'}`)}
|
||||
${overflowMenu ? '' : ' (R)'}`
|
||||
${isInOverflowMenu ? '' : ' (R)'}`
|
||||
}
|
||||
onClick = { _onToolbarToggleRaiseHand }
|
||||
toggled = { true } />
|
||||
@@ -175,11 +234,9 @@ const ReactionsMenu = ({
|
||||
*/
|
||||
function mapStateToProps(state: IReduxState) {
|
||||
const localParticipant = getLocalParticipant(state);
|
||||
const { isNarrowLayout } = state['features/base/responsive-ui'];
|
||||
|
||||
return {
|
||||
_localParticipantID: localParticipant?.id,
|
||||
_isMobile: isMobileBrowser() || isNarrowLayout,
|
||||
_isGifEnabled: isGifEnabled(state),
|
||||
_isGifMenuVisible: isGifsMenuOpen(state),
|
||||
_raisedHand: hasRaisedHand(localParticipant)
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import React, { ReactElement, useCallback } from 'react';
|
||||
import { WithTranslation } from 'react-i18next';
|
||||
import { connect, useSelector } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { isMobileBrowser } from '../../../base/environment/utils';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
import { IconArrowUp } from '../../../base/icons/svg';
|
||||
import ToolboxButtonWithIconPopup from '../../../base/toolbox/components/web/ToolboxButtonWithIconPopup';
|
||||
import { IconArrowUp, IconFaceSmile } from '../../../base/icons/svg';
|
||||
import AbstractButton, { type IProps as AbstractButtonProps } from '../../../base/toolbox/components/AbstractButton';
|
||||
import ToolboxButtonWithPopup from '../../../base/toolbox/components/web/ToolboxButtonWithPopup';
|
||||
import { toggleReactionsMenuVisibility } from '../../actions.web';
|
||||
import { IReactionEmojiProps } from '../../constants';
|
||||
import { getReactionsQueue, isReactionsEnabled } from '../../functions.any';
|
||||
import { getReactionsMenuVisibility } from '../../functions.web';
|
||||
import { getReactionsMenuVisibility, isReactionsButtonEnabled } from '../../functions.web';
|
||||
import { IReactionsMenuParent } from '../../types';
|
||||
|
||||
import RaiseHandButton from './RaiseHandButton';
|
||||
import ReactionEmoji from './ReactionEmoji';
|
||||
@@ -19,9 +21,14 @@ import ReactionsMenu from './ReactionsMenu';
|
||||
interface IProps extends WithTranslation {
|
||||
|
||||
/**
|
||||
* Whether or not reactions are enabled.
|
||||
* Whether a mobile browser is used or not.
|
||||
*/
|
||||
_reactionsEnabled: Boolean;
|
||||
_isMobile: boolean;
|
||||
|
||||
/**
|
||||
* Whether the reactions should be displayed on separate button or not.
|
||||
*/
|
||||
_reactionsButtonEnabled: boolean;
|
||||
|
||||
/**
|
||||
* The button's key.
|
||||
@@ -60,13 +67,28 @@ interface IProps extends WithTranslation {
|
||||
reactionsQueue: Array<IReactionEmojiProps>;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of a button for reactions.
|
||||
*/
|
||||
class ReactionsButtonImpl extends AbstractButton<AbstractButtonProps> {
|
||||
accessibilityLabel = 'toolbar.accessibilityLabel.reactions';
|
||||
icon = IconFaceSmile;
|
||||
label = 'toolbar.reactions';
|
||||
toggledLabel = 'toolbar.reactions';
|
||||
tooltip = 'toolbar.reactions';
|
||||
}
|
||||
|
||||
const ReactionsButton = translate(connect()(ReactionsButtonImpl));
|
||||
|
||||
/**
|
||||
* Button used for the reactions menu.
|
||||
*
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
function ReactionsMenuButton({
|
||||
_reactionsEnabled,
|
||||
_reactionsButtonEnabled,
|
||||
_isMobile,
|
||||
buttonKey,
|
||||
dispatch,
|
||||
handleClick,
|
||||
@@ -85,43 +107,69 @@ function ReactionsMenuButton({
|
||||
!visible && toggleReactionsMenu();
|
||||
}, [ visible, toggleReactionsMenu ]);
|
||||
|
||||
const closeReactionsMenu = useCallback(() => {
|
||||
visible && toggleReactionsMenu();
|
||||
}, [ visible, toggleReactionsMenu ]);
|
||||
|
||||
const reactionsMenu = (<div className = 'reactions-menu-container'>
|
||||
<ReactionsMenu />
|
||||
<ReactionsMenu parent = { IReactionsMenuParent.Button } />
|
||||
</div>);
|
||||
|
||||
return (
|
||||
<div className = 'reactions-menu-popup-container'>
|
||||
{!_reactionsEnabled || isNarrow ? (
|
||||
let content: ReactElement | null = null;
|
||||
|
||||
if (_reactionsButtonEnabled) {
|
||||
content = (
|
||||
<ToolboxButtonWithPopup
|
||||
ariaControls = 'reactions-menu-dialog'
|
||||
ariaExpanded = { isOpen }
|
||||
ariaHasPopup = { true }
|
||||
ariaLabel = { t('toolbar.accessibilityLabel.reactionsMenu') }
|
||||
onPopoverClose = { _isMobile ? closeReactionsMenu : toggleReactionsMenu }
|
||||
onPopoverOpen = { openReactionsMenu }
|
||||
popoverContent = { reactionsMenu }
|
||||
trigger = { _isMobile ? 'click' : undefined }
|
||||
visible = { visible }>
|
||||
<ReactionsButton
|
||||
buttonKey = { buttonKey }
|
||||
notifyMode = { notifyMode } />
|
||||
</ToolboxButtonWithPopup>);
|
||||
} else {
|
||||
content = isNarrow
|
||||
? (
|
||||
<RaiseHandButton
|
||||
buttonKey = { buttonKey }
|
||||
handleClick = { handleClick }
|
||||
notifyMode = { notifyMode } />)
|
||||
: (
|
||||
<ToolboxButtonWithIconPopup
|
||||
ariaControls = 'reactions-menu-dialog'
|
||||
ariaExpanded = { isOpen }
|
||||
ariaHasPopup = { true }
|
||||
ariaLabel = { t('toolbar.accessibilityLabel.reactionsMenu') }
|
||||
icon = { IconArrowUp }
|
||||
iconDisabled = { false }
|
||||
iconId = 'reactions-menu-button'
|
||||
onPopoverClose = { toggleReactionsMenu }
|
||||
onPopoverOpen = { openReactionsMenu }
|
||||
popoverContent = { reactionsMenu }
|
||||
visible = { visible }>
|
||||
<RaiseHandButton
|
||||
buttonKey = { buttonKey }
|
||||
handleClick = { handleClick }
|
||||
notifyMode = { notifyMode } />
|
||||
</ToolboxButtonWithIconPopup>
|
||||
)}
|
||||
: (
|
||||
<ToolboxButtonWithPopup
|
||||
ariaControls = 'reactions-menu-dialog'
|
||||
ariaExpanded = { isOpen }
|
||||
ariaHasPopup = { true }
|
||||
ariaLabel = { t('toolbar.accessibilityLabel.reactionsMenu') }
|
||||
icon = { IconArrowUp }
|
||||
iconDisabled = { false }
|
||||
iconId = 'reactions-menu-button'
|
||||
onPopoverClose = { toggleReactionsMenu }
|
||||
onPopoverOpen = { openReactionsMenu }
|
||||
popoverContent = { reactionsMenu }
|
||||
visible = { visible }>
|
||||
<RaiseHandButton
|
||||
buttonKey = { buttonKey }
|
||||
handleClick = { handleClick }
|
||||
notifyMode = { notifyMode } />
|
||||
</ToolboxButtonWithPopup>);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className = 'reactions-menu-popup-container'>
|
||||
{ content }
|
||||
{reactionsQueue.map(({ reaction, uid }, index) => (<ReactionEmoji
|
||||
index = { index }
|
||||
key = { uid }
|
||||
reaction = { reaction }
|
||||
uid = { uid } />))}
|
||||
</div>
|
||||
);
|
||||
</div>);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -134,9 +182,11 @@ function mapStateToProps(state: IReduxState) {
|
||||
const { isNarrowLayout } = state['features/base/responsive-ui'];
|
||||
|
||||
return {
|
||||
_reactionsButtonEnabled: isReactionsButtonEnabled(state),
|
||||
_reactionsEnabled: isReactionsEnabled(state),
|
||||
_isMobile: isMobileBrowser(),
|
||||
isOpen: getReactionsMenuVisibility(state),
|
||||
isNarrow: isMobileBrowser() || isNarrowLayout,
|
||||
isNarrow: isNarrowLayout,
|
||||
reactionsQueue: getReactionsQueue(state)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -8,9 +8,24 @@ import {
|
||||
} from './sounds';
|
||||
|
||||
/**
|
||||
* Reactions menu height on mobile web (px).
|
||||
* The height of the raise hand row in the reactions menu.
|
||||
*/
|
||||
export const REACTIONS_MENU_HEIGHT = 144;
|
||||
export const RAISE_HAND_ROW_HEIGHT = 54;
|
||||
|
||||
/**
|
||||
* The height of the gifs menu when displayed as part of the overflow menu.
|
||||
*/
|
||||
export const GIFS_MENU_HEIGHT_IN_OVERFLOW_MENU = 200;
|
||||
|
||||
/**
|
||||
* Reactions menu height when displayed as part of drawer.
|
||||
*/
|
||||
export const REACTIONS_MENU_HEIGHT_DRAWER = 144;
|
||||
|
||||
/**
|
||||
* Reactions menu height when displayed as part of overflow menu.
|
||||
*/
|
||||
export const REACTIONS_MENU_HEIGHT_IN_OVERFLOW_MENU = 106;
|
||||
|
||||
/**
|
||||
* The payload name for the datachannel/endpoint reaction event.
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { IReduxState } from '../app/types';
|
||||
import { getToolbarButtons } from '../base/config/functions.web';
|
||||
|
||||
import { isReactionsEnabled } from './functions.any';
|
||||
|
||||
export * from './functions.any';
|
||||
|
||||
@@ -11,3 +14,13 @@ export * from './functions.any';
|
||||
export function getReactionsMenuVisibility(state: IReduxState): boolean {
|
||||
return state['features/reactions'].visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the reactions button is enabled.
|
||||
*
|
||||
* @param {Object} state - The Redux state object.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isReactionsButtonEnabled(state: IReduxState) {
|
||||
return Boolean(getToolbarButtons(state).includes('reactions')) && isReactionsEnabled(state);
|
||||
}
|
||||
|
||||
5
react/features/reactions/types.ts
Normal file
5
react/features/reactions/types.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export enum IReactionsMenuParent {
|
||||
Button = 1,
|
||||
OverflowMenu = 2,
|
||||
OverflowDrawer = 3
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import { translate } from '../../../../base/i18n/functions';
|
||||
import { navigate }
|
||||
from '../../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
|
||||
import { screen } from '../../../../mobile/navigation/routes';
|
||||
import { IProps } from '../../LiveStream/AbstractStartLiveStreamDialog';
|
||||
import { IProps, _mapStateToProps as abstractMapStateToProps } from '../../LiveStream/AbstractStartLiveStreamDialog';
|
||||
import AbstractRecordButton, {
|
||||
IProps as AbstractProps,
|
||||
_mapStateToProps as _abstractMapStateToProps
|
||||
@@ -58,6 +58,7 @@ export function mapStateToProps(state: IReduxState) {
|
||||
|
||||
return {
|
||||
...abstractProps,
|
||||
...abstractMapStateToProps(state),
|
||||
visible: enabled && iosEnabled && abstractProps.visible
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Text, TouchableHighlight, View } from 'react-native';
|
||||
import { GestureResponderEvent, Text, TextStyle, TouchableHighlight, View, ViewStyle } from 'react-native';
|
||||
|
||||
import Icon from '../../../base/icons/components/Icon';
|
||||
import { RECORD_TYPE } from '../../constants';
|
||||
@@ -12,69 +10,71 @@ import styles from './styles';
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link RecordItem}.
|
||||
*/
|
||||
type Props = {
|
||||
interface IProps {
|
||||
|
||||
/**
|
||||
* The id of the record.
|
||||
*/
|
||||
id: String,
|
||||
id?: string;
|
||||
|
||||
/**
|
||||
* The name of the record.
|
||||
*/
|
||||
name: String,
|
||||
name?: string;
|
||||
|
||||
/**
|
||||
* The handler for the click event.
|
||||
*/
|
||||
onClick: Function,
|
||||
onClick?: (e?: GestureResponderEvent | React.MouseEvent) => void;
|
||||
|
||||
/**
|
||||
* The type of the record.
|
||||
*/
|
||||
type: String
|
||||
type?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Component to render Record data.
|
||||
*
|
||||
* @param {Props} props - The props of the component.
|
||||
* @param {IProps} props - The props of the component.
|
||||
* @returns {React$Element<any>}
|
||||
*/
|
||||
export const RecordItem = ({
|
||||
id,
|
||||
name,
|
||||
type,
|
||||
/* eslint-disable-next-line no-empty-function */
|
||||
/* eslint-disable-next-line @typescript-eslint/no-empty-function */
|
||||
onClick = () => {}
|
||||
}: Props) => {
|
||||
}: IProps) => {
|
||||
const { t } = useTranslation();
|
||||
const IconRecord = RECORD_TYPE[type].icon;
|
||||
const IconRecord = RECORD_TYPE[type ?? ''].icon;
|
||||
|
||||
return (
|
||||
<TouchableHighlight onPress = { onClick }>
|
||||
<View
|
||||
key = { `record-${id}` }
|
||||
style = { styles.recordItem }
|
||||
style = { styles.recordItem as ViewStyle }
|
||||
|
||||
// @ts-ignore
|
||||
title = { name }>
|
||||
<View style = { styles.recordTypeIcon }>
|
||||
<View style = { styles.recordTypeIcon as ViewStyle }>
|
||||
{IconRecord && (
|
||||
<Icon
|
||||
src = { IconRecord }
|
||||
style = { styles.recordIcon } />
|
||||
)}
|
||||
</View>
|
||||
<View style = { styles.recordDetails }>
|
||||
<View style = { styles.recordDetails as ViewStyle }>
|
||||
<Text
|
||||
key = { name }
|
||||
numberOfLines = { 1 }
|
||||
style = { styles.recordName }>
|
||||
style = { styles.recordName as TextStyle }>
|
||||
{name}
|
||||
</Text>
|
||||
<Text
|
||||
key = { type }
|
||||
style = { styles.recordType }>
|
||||
{t(RECORD_TYPE[type].label)}
|
||||
{t(RECORD_TYPE[type ?? ''].label)}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
@@ -1,8 +1,9 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Platform, SafeAreaView, ScrollView, Text, View } from 'react-native';
|
||||
import { Platform, SafeAreaView, ScrollView, Text, View, ViewStyle } from 'react-native';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { IconSearch } from '../../../base/icons/svg';
|
||||
import JitsiScreen from '../../../base/modal/components/JitsiScreen';
|
||||
import LoadingIndicator from '../../../base/react/components/native/LoadingIndicator';
|
||||
@@ -24,7 +25,7 @@ import styles from './styles';
|
||||
*/
|
||||
const SalesforceLinkDialog = () => {
|
||||
const { t } = useTranslation();
|
||||
const { clientHeight } = useSelector(state => state['features/base/responsive-ui']);
|
||||
const { clientHeight } = useSelector((state: IReduxState) => state['features/base/responsive-ui']);
|
||||
const {
|
||||
hasDetailsErrors,
|
||||
hasRecordsErrors,
|
||||
@@ -48,7 +49,7 @@ const SalesforceLinkDialog = () => {
|
||||
}, [ navigate, linkMeeting ]);
|
||||
|
||||
const renderSpinner = () => (
|
||||
<View style = { [ styles.recordsSpinner, { height: clientHeight - CONTENT_HEIGHT_OFFSET } ] }>
|
||||
<View style = { [ styles.recordsSpinner, { height: clientHeight - CONTENT_HEIGHT_OFFSET } ] as ViewStyle[] }>
|
||||
<LoadingIndicator />
|
||||
</View>
|
||||
);
|
||||
@@ -63,8 +64,8 @@ const SalesforceLinkDialog = () => {
|
||||
<SafeAreaView>
|
||||
<ScrollView
|
||||
bounces = { false }
|
||||
style = { [ styles.selectedRecord, { height: clientHeight - CONTENT_HEIGHT_OFFSET } ] }>
|
||||
<View style = { styles.recordInfo }>
|
||||
style = { [ styles.selectedRecord, { height: clientHeight - CONTENT_HEIGHT_OFFSET } ] as ViewStyle[] }>
|
||||
<View style = { styles.recordInfo as ViewStyle }>
|
||||
<RecordItem { ...selectedRecord } />
|
||||
{ selectedRecordOwner && <RecordItem { ...selectedRecordOwner } /> }
|
||||
{ hasDetailsErrors && renderDetailsErrors() }
|
||||
@@ -75,9 +76,9 @@ const SalesforceLinkDialog = () => {
|
||||
<Input
|
||||
customStyles = {{ container: styles.notes }}
|
||||
maxLength = { NOTES_MAX_LENGTH }
|
||||
minHeight = { Platform.OS === 'ios' && NOTES_LINES ? 20 * NOTES_LINES : null }
|
||||
minHeight = { Platform.OS === 'ios' && NOTES_LINES ? 20 * NOTES_LINES : undefined }
|
||||
multiline = { true }
|
||||
numberOfLines = { Platform.OS === 'ios' ? null : NOTES_LINES }
|
||||
numberOfLines = { Platform.OS === 'ios' ? undefined : NOTES_LINES }
|
||||
/* eslint-disable-next-line react/jsx-no-bind */
|
||||
onChange = { value => setNotes(value) }
|
||||
placeholder = { t('dialog.addMeetingNote') }
|
||||
@@ -87,14 +88,14 @@ const SalesforceLinkDialog = () => {
|
||||
);
|
||||
|
||||
const renderRecordsSearch = () => (
|
||||
<View style = { styles.recordsSearchContainer }>
|
||||
<View style = { styles.recordsSearchContainer as ViewStyle }>
|
||||
<Input
|
||||
icon = { IconSearch }
|
||||
maxLength = { NOTES_MAX_LENGTH }
|
||||
/* eslint-disable-next-line react/jsx-no-bind */
|
||||
onChange = { value => setSearchTerm(value) }
|
||||
placeholder = { t('dialog.searchInSalesforce') }
|
||||
value = { searchTerm } />
|
||||
value = { searchTerm ?? '' } />
|
||||
{(!isLoading && !hasRecordsErrors) && (
|
||||
<Text style = { styles.resultLabel }>
|
||||
{showSearchResults
|
||||
@@ -107,7 +108,7 @@ const SalesforceLinkDialog = () => {
|
||||
);
|
||||
|
||||
const renderNoRecords = () => showNoResults && (
|
||||
<View style = { [ styles.noRecords, { height: clientHeight - CONTENT_HEIGHT_OFFSET } ] }>
|
||||
<View style = { [ styles.noRecords, { height: clientHeight - CONTENT_HEIGHT_OFFSET } ] as ViewStyle[] }>
|
||||
<Text style = { styles.noRecordsText }>
|
||||
{t('dialog.searchResultsNotFound')}
|
||||
</Text>
|
||||
@@ -118,7 +119,7 @@ const SalesforceLinkDialog = () => {
|
||||
);
|
||||
|
||||
const renderRecordsError = () => (
|
||||
<View style = { [ styles.recordsError, { height: clientHeight - CONTENT_HEIGHT_OFFSET } ] }>
|
||||
<View style = { [ styles.recordsError, { height: clientHeight - CONTENT_HEIGHT_OFFSET } ] as ViewStyle[] }>
|
||||
<Text style = { styles.recordsErrorText }>
|
||||
{t('dialog.searchResultsError')}
|
||||
</Text>
|
||||
@@ -143,8 +144,8 @@ const SalesforceLinkDialog = () => {
|
||||
<SafeAreaView>
|
||||
<ScrollView
|
||||
bounces = { false }
|
||||
style = { [ styles.recordList, { height: clientHeight - LIST_HEIGHT_OFFSET } ] }>
|
||||
{records.map(item => (
|
||||
style = { [ styles.recordList, { height: clientHeight - LIST_HEIGHT_OFFSET } ] as ViewStyle[] }>
|
||||
{records.map((item: any) => (
|
||||
<RecordItem
|
||||
key = { `record-${item.id}` }
|
||||
/* eslint-disable-next-line react/jsx-no-bind */
|
||||
@@ -164,7 +165,7 @@ const SalesforceLinkDialog = () => {
|
||||
</View>
|
||||
{
|
||||
selectedRecord
|
||||
&& <View style = { styles.footer }>
|
||||
&& <View>
|
||||
<Button
|
||||
labelKey = 'dialog.Cancel'
|
||||
/* eslint-disable-next-line react/jsx-no-bind */
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { GestureResponderEvent } from 'react-native';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../app/types';
|
||||
@@ -21,7 +22,7 @@ import {
|
||||
interface ISelectedRecord {
|
||||
id: string;
|
||||
name: string;
|
||||
onClick: (e?: React.MouseEvent) => void;
|
||||
onClick: (e?: React.MouseEvent | GestureResponderEvent) => void;
|
||||
type: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import {
|
||||
Text,
|
||||
View
|
||||
TextStyle,
|
||||
View,
|
||||
ViewStyle
|
||||
} from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import { IReduxState, IStore } from '../../../../app/types';
|
||||
import { IJitsiConference } from '../../../../base/conference/reducer';
|
||||
import { getSecurityUiConfig } from '../../../../base/config/functions.any';
|
||||
import { MEETING_PASSWORD_ENABLED } from '../../../../base/flags/constants';
|
||||
import { getFeatureFlag } from '../../../../base/flags/functions';
|
||||
@@ -40,94 +43,94 @@ const _TEXT_INPUT_PROPS = {
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link SecurityDialog}.
|
||||
*/
|
||||
type Props = {
|
||||
interface IProps {
|
||||
|
||||
/**
|
||||
* The JitsiConference which requires a password.
|
||||
*/
|
||||
_conference: Object,
|
||||
_conference?: IJitsiConference;
|
||||
|
||||
/**
|
||||
* Whether the local user is the moderator.
|
||||
*/
|
||||
_isModerator: boolean,
|
||||
_isModerator: boolean;
|
||||
|
||||
/**
|
||||
* State of the lobby mode.
|
||||
*/
|
||||
_lobbyEnabled: boolean,
|
||||
_lobbyEnabled: boolean;
|
||||
|
||||
/**
|
||||
* Whether the lobby mode switch is available or not.
|
||||
*/
|
||||
_lobbyModeSwitchVisible: boolean,
|
||||
_lobbyModeSwitchVisible: boolean;
|
||||
|
||||
/**
|
||||
* The value for how the conference is locked (or undefined if not locked)
|
||||
* as defined by room-lock constants.
|
||||
*/
|
||||
_locked: string,
|
||||
_locked?: string;
|
||||
|
||||
/**
|
||||
* Checks if the conference room is locked or not.
|
||||
*/
|
||||
_lockedConference: boolean,
|
||||
_lockedConference: boolean;
|
||||
|
||||
/**
|
||||
* The current known password for the JitsiConference.
|
||||
*/
|
||||
_password: string,
|
||||
_password?: string;
|
||||
|
||||
/**
|
||||
* Number of digits used in the room-lock password.
|
||||
*/
|
||||
_passwordNumberOfDigits: number,
|
||||
_passwordNumberOfDigits?: number;
|
||||
|
||||
/**
|
||||
* Whether setting a room password is available or not.
|
||||
*/
|
||||
_roomPasswordControls: boolean,
|
||||
_roomPasswordControls: boolean;
|
||||
|
||||
/**
|
||||
* Redux store dispatch function.
|
||||
*/
|
||||
dispatch: Dispatch<any>,
|
||||
dispatch: IStore['dispatch'];
|
||||
|
||||
/**
|
||||
* Invoked to obtain translated strings.
|
||||
*/
|
||||
t: Function
|
||||
};
|
||||
t: Function;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} state of {@link SecurityDialog}.
|
||||
*/
|
||||
type State = {
|
||||
interface IState {
|
||||
|
||||
/**
|
||||
* Password added by the participant for room lock.
|
||||
*/
|
||||
passwordInputValue: string,
|
||||
passwordInputValue: string;
|
||||
|
||||
/**
|
||||
* Shows an input or a message.
|
||||
*/
|
||||
showElement: boolean
|
||||
};
|
||||
showElement: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Component that renders the security options dialog.
|
||||
*
|
||||
* @returns {React$Element<any>}
|
||||
*/
|
||||
class SecurityDialog extends PureComponent<Props, State> {
|
||||
class SecurityDialog extends PureComponent<IProps, IState> {
|
||||
|
||||
/**
|
||||
* Instantiates a new {@code SecurityDialog}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
@@ -180,8 +183,8 @@ class SecurityDialog extends PureComponent<Props, State> {
|
||||
<Text style = { styles.lobbyModeText }>
|
||||
{ t('lobby.enableDialogText') }
|
||||
</Text>
|
||||
<View style = { styles.lobbyModeSection }>
|
||||
<Text style = { styles.lobbyModeLabel } >
|
||||
<View style = { styles.lobbyModeSection as ViewStyle }>
|
||||
<Text style = { styles.lobbyModeLabel as TextStyle } >
|
||||
{ t('lobby.toggleLabel') }
|
||||
</Text>
|
||||
<Switch
|
||||
@@ -267,7 +270,7 @@ class SecurityDialog extends PureComponent<Props, State> {
|
||||
if (_locked === LOCKED_REMOTELY) {
|
||||
if (_isModerator) {
|
||||
setPasswordControls = (
|
||||
<View style = { styles.passwordSetRemotelyContainer }>
|
||||
<View style = { styles.passwordSetRemotelyContainer as ViewStyle }>
|
||||
<Text style = { styles.passwordSetRemotelyText }>
|
||||
{ t('passwordSetRemotely') }
|
||||
</Text>
|
||||
@@ -281,7 +284,7 @@ class SecurityDialog extends PureComponent<Props, State> {
|
||||
);
|
||||
} else {
|
||||
setPasswordControls = (
|
||||
<View style = { styles.passwordSetRemotelyContainer }>
|
||||
<View style = { styles.passwordSetRemotelyContainer as ViewStyle }>
|
||||
<Text style = { styles.passwordSetRemotelyTextDisabled }>
|
||||
{ t('passwordSetRemotely') }
|
||||
</Text>
|
||||
@@ -306,7 +309,7 @@ class SecurityDialog extends PureComponent<Props, State> {
|
||||
<View
|
||||
style = {
|
||||
_locked !== LOCKED_REMOTELY
|
||||
&& styles.passwordContainerControls
|
||||
&& styles.passwordContainerControls as ViewStyle
|
||||
}>
|
||||
<View>
|
||||
{ this._setRoomPasswordMessage() }
|
||||
@@ -324,7 +327,7 @@ class SecurityDialog extends PureComponent<Props, State> {
|
||||
* @private
|
||||
*/
|
||||
_setRoomPasswordMessage() {
|
||||
let textInputProps = _TEXT_INPUT_PROPS;
|
||||
let textInputProps: any = _TEXT_INPUT_PROPS;
|
||||
const {
|
||||
_isModerator,
|
||||
_locked,
|
||||
@@ -362,8 +365,8 @@ class SecurityDialog extends PureComponent<Props, State> {
|
||||
} else if (_locked) {
|
||||
if (_locked === LOCKED_LOCALLY && typeof _password !== 'undefined') {
|
||||
return (
|
||||
<View style = { styles.savedPasswordContainer }>
|
||||
<Text style = { styles.savedPasswordLabel }>
|
||||
<View style = { styles.savedPasswordContainer as ViewStyle }>
|
||||
<Text style = { styles.savedPasswordLabel as TextStyle }>
|
||||
{ t('info.password') }
|
||||
</Text>
|
||||
<Text style = { styles.savedPassword }>
|
||||
@@ -376,8 +379,6 @@ class SecurityDialog extends PureComponent<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
_onToggleLobbyMode: () => void;
|
||||
|
||||
/**
|
||||
* Handles the enable-disable lobby mode switch.
|
||||
*
|
||||
@@ -394,8 +395,6 @@ class SecurityDialog extends PureComponent<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
_onAddPassword: () => void;
|
||||
|
||||
/**
|
||||
* Callback to be invoked when add password button is pressed.
|
||||
*
|
||||
@@ -431,15 +430,13 @@ class SecurityDialog extends PureComponent<Props, State> {
|
||||
return true;
|
||||
}
|
||||
|
||||
_onChangeText: string => void;
|
||||
|
||||
/**
|
||||
* Callback to be invoked when the text in the field changes.
|
||||
*
|
||||
* @param {string} passwordInputValue - The value of password input.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onChangeText(passwordInputValue) {
|
||||
_onChangeText(passwordInputValue: string) {
|
||||
if (!this._validateInputValue(passwordInputValue)) {
|
||||
return;
|
||||
}
|
||||
@@ -449,8 +446,6 @@ class SecurityDialog extends PureComponent<Props, State> {
|
||||
});
|
||||
}
|
||||
|
||||
_onCancel: () => void;
|
||||
|
||||
/**
|
||||
* Cancels value typed in text input.
|
||||
*
|
||||
@@ -465,8 +460,6 @@ class SecurityDialog extends PureComponent<Props, State> {
|
||||
this.props.dispatch(unlockRoom());
|
||||
}
|
||||
|
||||
_onCopy: () => void;
|
||||
|
||||
/**
|
||||
* Copies room password.
|
||||
*
|
||||
@@ -478,8 +471,6 @@ class SecurityDialog extends PureComponent<Props, State> {
|
||||
copyText(passwordInputValue);
|
||||
}
|
||||
|
||||
_onSubmit: () => void;
|
||||
|
||||
/**
|
||||
* Submits value typed in text input.
|
||||
*
|
||||
@@ -492,7 +483,7 @@ class SecurityDialog extends PureComponent<Props, State> {
|
||||
} = this.props;
|
||||
const { passwordInputValue } = this.state;
|
||||
|
||||
dispatch(endRoomLockRequest(_conference, passwordInputValue));
|
||||
_conference && dispatch(endRoomLockRequest(_conference, passwordInputValue));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -500,14 +491,14 @@ class SecurityDialog extends PureComponent<Props, State> {
|
||||
* Maps part of the Redux state to the props of this component.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @returns {Props}
|
||||
* @returns {IProps}
|
||||
*/
|
||||
function _mapStateToProps(state: Object): Object {
|
||||
function _mapStateToProps(state: IReduxState) {
|
||||
const { conference, locked, password } = state['features/base/conference'];
|
||||
const { disableLobbyPassword, hideLobbyButton } = getSecurityUiConfig(state);
|
||||
const { lobbyEnabled } = state['features/lobby'];
|
||||
const { roomPasswordNumberOfDigits } = state['features/base/config'];
|
||||
const lobbySupported = conference && conference.isLobbySupported();
|
||||
const lobbySupported = conference?.isLobbySupported();
|
||||
const visible = getFeatureFlag(state, MEETING_PASSWORD_ENABLED, true);
|
||||
|
||||
return {
|
||||
@@ -1,21 +1,17 @@
|
||||
// @flow
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { translate } from '../../../../base/i18n/functions';
|
||||
import { navigate } from '../../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
|
||||
import { screen } from '../../../../mobile/navigation/routes';
|
||||
import AbstractSecurityDialogButton, {
|
||||
type Props as AbstractSecurityDialogButtonProps,
|
||||
IProps as AbstractSecurityDialogButtonProps,
|
||||
_mapStateToProps as _abstractMapStateToProps
|
||||
} from '../AbstractSecurityDialogButton';
|
||||
|
||||
type Props = AbstractSecurityDialogButtonProps;
|
||||
|
||||
/**
|
||||
* Implements an {@link AbstractSecurityDialogButton} to open the security screen.
|
||||
*/
|
||||
class SecurityDialogButton<P: Props, S:*> extends AbstractSecurityDialogButton<P, S> {
|
||||
class SecurityDialogButton<P extends AbstractSecurityDialogButtonProps, S> extends AbstractSecurityDialogButton<P, S> {
|
||||
|
||||
/**
|
||||
* Opens / closes the security screen.
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
import BaseTheme from '../../../../base/ui/components/BaseTheme.native';
|
||||
|
||||
/**
|
||||
@@ -1,7 +1,6 @@
|
||||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
import { WithTranslation } from 'react-i18next';
|
||||
import { Text, View, ViewStyle } from 'react-native';
|
||||
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
|
||||
@@ -10,45 +9,40 @@ import styles, { ANDROID_UNDERLINE_COLOR, PLACEHOLDER_COLOR } from './styles';
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link FormRow}.
|
||||
*/
|
||||
type Props = {
|
||||
interface IProps extends WithTranslation {
|
||||
|
||||
/**
|
||||
*
|
||||
* Component's children.
|
||||
*/
|
||||
children: Object,
|
||||
children: React.ReactElement;
|
||||
|
||||
/**
|
||||
* Prop to decide if a row separator is to be rendered.
|
||||
*/
|
||||
fieldSeparator: boolean,
|
||||
fieldSeparator?: boolean;
|
||||
|
||||
/**
|
||||
* The i18n key of the text label of the form field.
|
||||
*/
|
||||
label: string,
|
||||
label: string;
|
||||
|
||||
/**
|
||||
* One of 'row' (default) or 'column'.
|
||||
*/
|
||||
layout: string,
|
||||
|
||||
/**
|
||||
* Invoked to obtain translated strings.
|
||||
*/
|
||||
t: Function
|
||||
layout?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements a React {@code Component} which renders a standardized row on a
|
||||
* form. The component should have exactly one child component.
|
||||
*/
|
||||
class FormRow extends Component<Props> {
|
||||
class FormRow extends Component<IProps> {
|
||||
/**
|
||||
* Initializes a new {@code FormRow} instance.
|
||||
*
|
||||
* @param {Object} props - Component properties.
|
||||
*/
|
||||
constructor(props) {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
React.Children.only(this.props.children);
|
||||
@@ -77,7 +71,7 @@ class FormRow extends Component<Props> {
|
||||
return (
|
||||
<View
|
||||
style = { this._getRowStyle() } >
|
||||
<View style = { styles.fieldLabelContainer } >
|
||||
<View style = { styles.fieldLabelContainer as ViewStyle } >
|
||||
<Text
|
||||
style = { [
|
||||
styles.text,
|
||||
@@ -87,15 +81,13 @@ class FormRow extends Component<Props> {
|
||||
{ t(this.props.label) }
|
||||
</Text>
|
||||
</View>
|
||||
<View style = { styles.fieldValueContainer } >
|
||||
<View style = { styles.fieldValueContainer as ViewStyle } >
|
||||
{ newChild }
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_getDefaultFieldProps: (field: Component<*, *>) => Object;
|
||||
|
||||
/**
|
||||
* Assembles the default props to the field child component of this form
|
||||
* row.
|
||||
@@ -108,8 +100,8 @@ class FormRow extends Component<Props> {
|
||||
* @private
|
||||
* @returns {Object}
|
||||
*/
|
||||
_getDefaultFieldProps(field: Object) {
|
||||
if (field && field.type) {
|
||||
_getDefaultFieldProps(field?: React.ReactElement) {
|
||||
if (field?.type) { // @ts-ignore
|
||||
switch (field.type.displayName) {
|
||||
case 'TextInput':
|
||||
return {
|
||||
@@ -126,8 +118,6 @@ class FormRow extends Component<Props> {
|
||||
return {};
|
||||
}
|
||||
|
||||
_getRowStyle: () => Array<Object>;
|
||||
|
||||
/**
|
||||
* Assembles the row style array based on the row's props.
|
||||
*
|
||||
@@ -136,8 +126,8 @@ class FormRow extends Component<Props> {
|
||||
*/
|
||||
_getRowStyle() {
|
||||
const { fieldSeparator, layout } = this.props;
|
||||
const rowStyle = [
|
||||
styles.fieldContainer
|
||||
const rowStyle: ViewStyle[] = [
|
||||
styles.fieldContainer as ViewStyle
|
||||
];
|
||||
|
||||
if (fieldSeparator) {
|
||||
@@ -146,7 +136,7 @@ class FormRow extends Component<Props> {
|
||||
|
||||
if (layout === 'column') {
|
||||
rowStyle.push(
|
||||
styles.fieldContainerColumn
|
||||
styles.fieldContainerColumn as ViewStyle
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import { Text, View } from 'react-native';
|
||||
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
|
||||
// @ts-ignore
|
||||
import styles from './styles';
|
||||
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ import { BUTTON_TYPES } from '../../../base/ui/constants.any';
|
||||
import { AVATAR_SIZE } from '../../../welcome/components/styles';
|
||||
import { isServerURLChangeEnabled, normalizeUserInputURL } from '../../functions.native';
|
||||
|
||||
// @ts-ignore
|
||||
import FormRow from './FormRow';
|
||||
import FormSection from './FormSection';
|
||||
import styles from './styles';
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// @flow
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { VIDEO_SHARE_BUTTON_ENABLED } from '../../../base/flags/constants';
|
||||
import { getFeatureFlag } from '../../../base/flags/functions';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
@@ -15,30 +13,25 @@ import { isSharingStatus } from '../../functions';
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link TileViewButton}.
|
||||
*/
|
||||
type Props = AbstractButtonProps & {
|
||||
interface IProps extends AbstractButtonProps {
|
||||
|
||||
/**
|
||||
* Whether or not the button is disabled.
|
||||
*/
|
||||
_isDisabled: boolean,
|
||||
_isDisabled: boolean;
|
||||
|
||||
/**
|
||||
* Whether or not the local participant is sharing a video.
|
||||
*/
|
||||
_sharingVideo: boolean,
|
||||
|
||||
/**
|
||||
* The redux {@code dispatch} function.
|
||||
*/
|
||||
dispatch: Dispatch<any>
|
||||
};
|
||||
_sharingVideo: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Component that renders a toolbar button for toggling the tile layout view.
|
||||
*
|
||||
* @augments AbstractButton
|
||||
*/
|
||||
class VideoShareButton extends AbstractButton<Props, *> {
|
||||
class VideoShareButton extends AbstractButton<IProps> {
|
||||
accessibilityLabel = 'toolbar.accessibilityLabel.sharedvideo';
|
||||
icon = IconPlay;
|
||||
label = 'toolbar.sharedvideo';
|
||||
@@ -94,17 +87,17 @@ class VideoShareButton extends AbstractButton<Props, *> {
|
||||
* @param {Object} state - The Redux state.
|
||||
* @param {Object} ownProps - The properties explicitly passed to the component instance.
|
||||
* @private
|
||||
* @returns {Props}
|
||||
* @returns {IProps}
|
||||
*/
|
||||
function _mapStateToProps(state, ownProps): Object {
|
||||
function _mapStateToProps(state: IReduxState, ownProps: any) {
|
||||
const { ownerId, status: sharedVideoStatus } = state['features/shared-video'];
|
||||
const localParticipantId = getLocalParticipant(state).id;
|
||||
const localParticipantId = getLocalParticipant(state)?.id;
|
||||
const enabled = getFeatureFlag(state, VIDEO_SHARE_BUTTON_ENABLED, true);
|
||||
const { visible = enabled } = ownProps;
|
||||
|
||||
if (ownerId !== localParticipantId) {
|
||||
return {
|
||||
_isDisabled: isSharingStatus(sharedVideoStatus),
|
||||
_isDisabled: isSharingStatus(sharedVideoStatus ?? ''),
|
||||
_sharingVideo: false,
|
||||
visible
|
||||
};
|
||||
@@ -112,7 +105,7 @@ function _mapStateToProps(state, ownProps): Object {
|
||||
|
||||
return {
|
||||
_isDisabled: false,
|
||||
_sharingVideo: isSharingStatus(sharedVideoStatus),
|
||||
_sharingVideo: isSharingStatus(sharedVideoStatus ?? ''),
|
||||
visible
|
||||
};
|
||||
}
|
||||
@@ -1,22 +1,24 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import InputDialog from '../../../base/dialog/components/native/InputDialog';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
import AbstractSharedVideoDialog from '../AbstractSharedVideoDialog';
|
||||
import AbstractSharedVideoDialog, { IProps } from '../AbstractSharedVideoDialog';
|
||||
|
||||
interface IState {
|
||||
error: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements a component to render a display name prompt.
|
||||
*/
|
||||
class SharedVideoDialog extends AbstractSharedVideoDialog<*> {
|
||||
class SharedVideoDialog extends AbstractSharedVideoDialog<IState> {
|
||||
/**
|
||||
* Instantiates a new component.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props) {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
@@ -26,15 +28,13 @@ class SharedVideoDialog extends AbstractSharedVideoDialog<*> {
|
||||
this._onSubmitValue = this._onSubmitValue.bind(this);
|
||||
}
|
||||
|
||||
_onSubmitValue: () => boolean;
|
||||
|
||||
/**
|
||||
* Callback to be invoked when the value of the link input is submitted.
|
||||
*
|
||||
* @param {string} value - The entered video link.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_onSubmitValue(value) {
|
||||
_onSubmitValue(value: string) {
|
||||
const result = super._onSetVideoLink(value);
|
||||
|
||||
if (!result) {
|
||||
@@ -2,8 +2,7 @@ import React, { useEffect } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import JitsiScreen from '../../../base/modal/components/JitsiScreen';
|
||||
import { resetSearchCriteria } from '../../actions';
|
||||
|
||||
import { resetSearchCriteria } from '../../actions.native';
|
||||
|
||||
import SpeakerStatsList from './SpeakerStatsList';
|
||||
import SpeakerStatsSearch from './SpeakerStatsSearch';
|
||||
@@ -1,9 +1,8 @@
|
||||
// @flow
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { createToolbarEvent } from '../../../analytics/AnalyticsEvents';
|
||||
import { sendAnalytics } from '../../../analytics/functions';
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { SPEAKERSTATS_ENABLED } from '../../../base/flags/constants';
|
||||
import { getFeatureFlag } from '../../../base/flags/functions';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
@@ -39,7 +38,7 @@ class SpeakerStatsButton extends AbstractSpeakerStatsButton {
|
||||
* visible: boolean
|
||||
* }}
|
||||
*/
|
||||
function _mapStateToProps(state): Object {
|
||||
function _mapStateToProps(state: IReduxState) {
|
||||
const enabled = getFeatureFlag(state, SPEAKERSTATS_ENABLED, true);
|
||||
|
||||
return {
|
||||
@@ -1,7 +1,5 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
import { Text, View, ViewStyle } from 'react-native';
|
||||
|
||||
import Avatar from '../../../base/avatar/components/Avatar';
|
||||
import StatelessAvatar from '../../../base/avatar/components/native/StatelessAvatar';
|
||||
@@ -11,46 +9,44 @@ import BaseTheme from '../../../base/ui/components/BaseTheme.native';
|
||||
import TimeElapsed from './TimeElapsed';
|
||||
import style from './styles';
|
||||
|
||||
type Props = {
|
||||
interface IProps {
|
||||
|
||||
/**
|
||||
* The name of the participant.
|
||||
*/
|
||||
displayName: string,
|
||||
displayName: string;
|
||||
|
||||
/**
|
||||
* The total milliseconds the participant has been dominant speaker.
|
||||
*/
|
||||
dominantSpeakerTime: number,
|
||||
|
||||
/**
|
||||
* The id of the user.
|
||||
*/
|
||||
participantId: string,
|
||||
dominantSpeakerTime: number;
|
||||
|
||||
/**
|
||||
* True if the participant is no longer in the meeting.
|
||||
*/
|
||||
hasLeft: boolean,
|
||||
hasLeft: boolean;
|
||||
|
||||
/**
|
||||
* True if the participant is currently the dominant speaker.
|
||||
*/
|
||||
isDominantSpeaker: boolean
|
||||
};
|
||||
isDominantSpeaker: boolean;
|
||||
|
||||
const SpeakerStatsItem = (props: Props) =>
|
||||
/**
|
||||
* The id of the user.
|
||||
*/
|
||||
participantId: string;
|
||||
}
|
||||
|
||||
const SpeakerStatsItem = (props: IProps) =>
|
||||
(
|
||||
<View
|
||||
key = { props.participantId }
|
||||
style = { style.speakerStatsItemContainer }>
|
||||
style = { style.speakerStatsItemContainer as ViewStyle }>
|
||||
<View style = { style.speakerStatsAvatar }>
|
||||
{
|
||||
props.hasLeft ? (
|
||||
<StatelessAvatar
|
||||
className = 'userAvatar'
|
||||
color = { BaseTheme.palette.ui05 }
|
||||
id = 'avatar'
|
||||
initials = { getInitials(props.displayName) }
|
||||
size = { BaseTheme.spacing[5] } />
|
||||
) : (
|
||||
@@ -61,7 +57,7 @@ const SpeakerStatsItem = (props: Props) =>
|
||||
)
|
||||
}
|
||||
</View>
|
||||
<View style = { style.speakerStatsNameTime } >
|
||||
<View style = { style.speakerStatsNameTime as ViewStyle } >
|
||||
<Text style = { [ style.speakerStatsText, props.hasLeft && style.speakerStatsLeft ] }>
|
||||
{props.displayName}
|
||||
</Text>
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import { View } from 'react-native';
|
||||
|
||||
@@ -22,5 +20,4 @@ const SpeakerStatsList = () => {
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
export default SpeakerStatsList;
|
||||
@@ -6,12 +6,11 @@ import { useDispatch, useSelector } from 'react-redux';
|
||||
import { IconSearch } from '../../../base/icons/svg';
|
||||
import Input from '../../../base/ui/components/native/Input';
|
||||
import { escapeRegexp } from '../../../base/util/helpers';
|
||||
import { initSearch } from '../../actions';
|
||||
import { initSearch } from '../../actions.native';
|
||||
import { isSpeakerStatsSearchDisabled } from '../../functions';
|
||||
|
||||
import styles from './styles';
|
||||
|
||||
|
||||
/**
|
||||
* React component for display an individual user's speaker stats.
|
||||
*
|
||||
@@ -1,6 +1,5 @@
|
||||
/* @flow */
|
||||
|
||||
import React, { PureComponent } from 'react';
|
||||
import { WithTranslation } from 'react-i18next';
|
||||
import { Text } from 'react-native';
|
||||
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
@@ -9,23 +8,18 @@ import { createLocalizedTime } from '../timeFunctions';
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link TimeElapsed}.
|
||||
*/
|
||||
type Props = {
|
||||
interface IProps extends WithTranslation {
|
||||
|
||||
/**
|
||||
* Style for text.
|
||||
*/
|
||||
style: Object,
|
||||
|
||||
/**
|
||||
* The function to translate human-readable text.
|
||||
*/
|
||||
t: Function,
|
||||
style: Object;
|
||||
|
||||
/**
|
||||
* The milliseconds to be converted into a human-readable format.
|
||||
*/
|
||||
time: number
|
||||
};
|
||||
time: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* React component for displaying total time elapsed. Converts a total count of
|
||||
@@ -34,7 +28,7 @@ type Props = {
|
||||
*
|
||||
* @augments Component
|
||||
*/
|
||||
class TimeElapsed extends PureComponent<Props> {
|
||||
class TimeElapsed extends PureComponent<IProps> {
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
@@ -1,12 +1,8 @@
|
||||
/* eslint-disable lines-around-comment */
|
||||
|
||||
import React, { ReactElement } from 'react';
|
||||
import { GestureResponderEvent, StyleProp } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
// @ts-ignore
|
||||
import Container from '../../../base/react/components/native/Container';
|
||||
// @ts-ignore
|
||||
import Text from '../../../base/react/components/native/Text';
|
||||
import {
|
||||
AbstractCaptions,
|
||||
@@ -14,10 +10,8 @@ import {
|
||||
_abstractMapStateToProps
|
||||
} from '../AbstractCaptions';
|
||||
|
||||
// @ts-ignore
|
||||
import styles from './styles';
|
||||
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link Captions}.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/* eslint-disable lines-around-comment */
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
@@ -8,17 +6,13 @@ import { getFeatureFlag } from '../../../base/flags/functions';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
import { IconSubtitles } from '../../../base/icons/svg';
|
||||
import { navigate }
|
||||
// @ts-ignore
|
||||
from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
|
||||
// @ts-ignore
|
||||
import { screen } from '../../../mobile/navigation/routes';
|
||||
import {
|
||||
AbstractClosedCaptionButton,
|
||||
IAbstractProps,
|
||||
_abstractMapStateToProps
|
||||
} from '../AbstractClosedCaptionButton';
|
||||
|
||||
|
||||
/**
|
||||
* A button which starts/stops the transcriptions.
|
||||
*/
|
||||
@@ -52,7 +46,7 @@ class ClosedCaptionButton
|
||||
* @private
|
||||
* @returns {Props}
|
||||
*/
|
||||
export function mapStateToProps(state: IReduxState, ownProps: IAbstractProps) {
|
||||
export function mapStateToProps(state: IReduxState, ownProps: any) {
|
||||
const enabled = getFeatureFlag(state, CLOSE_CAPTIONS_ENABLED, true);
|
||||
const abstractProps = _abstractMapStateToProps(state, ownProps);
|
||||
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
/* eslint-disable lines-around-comment */
|
||||
|
||||
import React from 'react';
|
||||
import { ScrollView } from 'react-native';
|
||||
|
||||
import LanguageListItem from './LanguageListItem';
|
||||
// @ts-ignore
|
||||
import styles from './styles';
|
||||
|
||||
interface ILanguageListProps {
|
||||
@@ -13,7 +10,6 @@ interface ILanguageListProps {
|
||||
selectedLanguage: string;
|
||||
}
|
||||
|
||||
|
||||
interface ILanguageItem {
|
||||
id: string;
|
||||
lang: string;
|
||||
@@ -30,7 +26,6 @@ const LanguageList = ({ items, onLanguageSelected }: ILanguageListProps) => {
|
||||
const listItems = items?.map(item => (
|
||||
<LanguageListItem
|
||||
key = { item.id }
|
||||
// @ts-ignore
|
||||
lang = { item.lang }
|
||||
onLanguageSelected = { onLanguageSelected }
|
||||
selected = { item.selected } />
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/* eslint-disable lines-around-comment */
|
||||
|
||||
import React, { useCallback } from 'react';
|
||||
import { WithTranslation } from 'react-i18next';
|
||||
import { StyleProp, TouchableHighlight, View, ViewStyle } from 'react-native';
|
||||
@@ -9,7 +7,6 @@ import { translate } from '../../../base/i18n/functions';
|
||||
import Icon from '../../../base/icons/components/Icon';
|
||||
import { IconCheck } from '../../../base/icons/svg';
|
||||
|
||||
// @ts-ignore
|
||||
import styles from './styles';
|
||||
|
||||
|
||||
|
||||
@@ -1,21 +1,15 @@
|
||||
/* eslint-disable lines-around-comment */
|
||||
|
||||
import React, { useCallback } from 'react';
|
||||
|
||||
// @ts-ignore
|
||||
import JitsiScreen from '../../../base/modal/components/JitsiScreen';
|
||||
import { goBack }
|
||||
// @ts-ignore
|
||||
from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
|
||||
import AbstractLanguageSelectorDialog, {
|
||||
IAbstractLanguageSelectorDialogProps
|
||||
} from '../AbstractLanguageSelectorDialog';
|
||||
|
||||
import LanguageList from './LanguageList';
|
||||
// @ts-ignore
|
||||
import styles from './styles';
|
||||
|
||||
|
||||
const LanguageSelectorDialog = (props: IAbstractLanguageSelectorDialogProps) => {
|
||||
const { language, listItems, onLanguageSelected, subtitles } = props;
|
||||
|
||||
@@ -25,7 +19,6 @@ const LanguageSelectorDialog = (props: IAbstractLanguageSelectorDialogProps) =>
|
||||
}, [ language ]);
|
||||
|
||||
return (
|
||||
// @ts-ignore
|
||||
<JitsiScreen
|
||||
disableForcedKeyboardDismiss = { true }
|
||||
style = { styles.subtitlesContainer }>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// @flow
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { setAudioOnly, toggleAudioOnly } from '../../../base/audio-only/actions';
|
||||
import { AUDIO_ONLY_BUTTON_ENABLED } from '../../../base/flags/constants';
|
||||
import { getFeatureFlag } from '../../../base/flags/functions';
|
||||
@@ -16,28 +15,23 @@ import { screen } from '../../../mobile/navigation/routes';
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link AudioOnlyButton}.
|
||||
*/
|
||||
type Props = AbstractButtonProps & {
|
||||
interface IProps extends AbstractButtonProps {
|
||||
|
||||
/**
|
||||
* Whether the current conference is in audio only mode or not.
|
||||
*/
|
||||
_audioOnly: boolean,
|
||||
_audioOnly: boolean;
|
||||
|
||||
/**
|
||||
* Indicates whether the car mode is enabled.
|
||||
*/
|
||||
_startCarMode: boolean,
|
||||
|
||||
/**
|
||||
* The redux {@code dispatch} function.
|
||||
*/
|
||||
dispatch: Function
|
||||
};
|
||||
_startCarMode?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* An implementation of a button for toggling the audio-only mode.
|
||||
*/
|
||||
class AudioOnlyButton extends AbstractButton<Props, *> {
|
||||
class AudioOnlyButton extends AbstractButton<IProps> {
|
||||
accessibilityLabel = 'toolbar.accessibilityLabel.audioOnly';
|
||||
icon = IconAudioOnly;
|
||||
label = 'toolbar.audioOnlyOn';
|
||||
@@ -86,7 +80,7 @@ class AudioOnlyButton extends AbstractButton<Props, *> {
|
||||
* _audioOnly: boolean
|
||||
* }}
|
||||
*/
|
||||
function _mapStateToProps(state, ownProps): Object {
|
||||
function _mapStateToProps(state: IReduxState, ownProps: any) {
|
||||
const { enabled: audioOnly } = state['features/base/audio-only'];
|
||||
const enabledInFeatureFlags = getFeatureFlag(state, AUDIO_ONLY_BUTTON_ENABLED, true);
|
||||
const { startCarMode } = state['features/base/settings'];
|
||||
@@ -1,17 +1,14 @@
|
||||
/* eslint-disable lines-around-comment */
|
||||
import React, { useCallback } from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { createBreakoutRoomsEvent, createToolbarEvent } from '../../../analytics/AnalyticsEvents';
|
||||
import { sendAnalytics } from '../../../analytics/functions';
|
||||
// @ts-ignore
|
||||
import { appNavigate } from '../../../app/actions';
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import ColorSchemeRegistry from '../../../base/color-scheme/ColorSchemeRegistry';
|
||||
import { endConference } from '../../../base/conference/actions';
|
||||
import { hideSheet } from '../../../base/dialog/actions';
|
||||
// @ts-ignore
|
||||
import BottomSheet from '../../../base/dialog/components/native/BottomSheet';
|
||||
import { PARTICIPANT_ROLE } from '../../../base/participants/constants';
|
||||
import { getLocalParticipant } from '../../../base/participants/functions';
|
||||
|
||||
@@ -8,7 +8,6 @@ import { BUTTON_TYPES } from '../../../base/ui/constants.native';
|
||||
|
||||
import HangupMenu from './HangupMenu';
|
||||
|
||||
|
||||
/**
|
||||
* Button for showing the hangup menu.
|
||||
*
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
// @flow
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { createToolbarEvent } from '../../../analytics/AnalyticsEvents';
|
||||
import { sendAnalytics } from '../../../analytics/functions';
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
import { IconCloudUpload } from '../../../base/icons/svg';
|
||||
import AbstractButton, { IProps as AbstractButtonProps } from '../../../base/toolbox/components/AbstractButton';
|
||||
@@ -15,7 +14,7 @@ import { isSalesforceEnabled } from '../../../salesforce/functions';
|
||||
/**
|
||||
* Implementation of a button for opening the Salesforce link dialog.
|
||||
*/
|
||||
class LinkToSalesforceButton extends AbstractButton<AbstractButtonProps, *> {
|
||||
class LinkToSalesforceButton extends AbstractButton<AbstractButtonProps> {
|
||||
accessibilityLabel = 'toolbar.accessibilityLabel.linkToSalesforce';
|
||||
icon = IconCloudUpload;
|
||||
label = 'toolbar.linkToSalesforce';
|
||||
@@ -40,7 +39,7 @@ class LinkToSalesforceButton extends AbstractButton<AbstractButtonProps, *> {
|
||||
* @private
|
||||
* @returns {Props}
|
||||
*/
|
||||
function mapStateToProps(state) {
|
||||
function mapStateToProps(state: IReduxState) {
|
||||
return {
|
||||
visible: isSalesforceEnabled(state)
|
||||
};
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable lines-around-comment */
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
@@ -8,9 +7,7 @@ import { translate } from '../../../base/i18n/functions';
|
||||
import { IconCar } from '../../../base/icons/svg';
|
||||
import AbstractButton, { IProps as AbstractButtonProps } from '../../../base/toolbox/components/AbstractButton';
|
||||
import { navigate }
|
||||
// @ts-ignore
|
||||
from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
|
||||
// @ts-ignore
|
||||
import { screen } from '../../../mobile/navigation/routes';
|
||||
/* eslint-enable lines-around-comment */
|
||||
|
||||
@@ -50,5 +47,4 @@ function _mapStateToProps(state: IReduxState, ownProps: AbstractButtonProps): Ob
|
||||
};
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
export default translate(connect(_mapStateToProps)(OpenCarmodeButton));
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// @flow
|
||||
|
||||
import React, { PureComponent } from 'react';
|
||||
import { ViewStyle } from 'react-native';
|
||||
import { Divider } from 'react-native-paper';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState, IStore } from '../../../app/types';
|
||||
import { hideSheet } from '../../../base/dialog/actions';
|
||||
import BottomSheet from '../../../base/dialog/components/native/BottomSheet';
|
||||
import { bottomSheetStyles } from '../../../base/dialog/components/native/styles';
|
||||
@@ -32,58 +32,58 @@ import ScreenSharingButton from './ScreenSharingButton';
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link OverflowMenu}.
|
||||
*/
|
||||
type Props = {
|
||||
interface IProps {
|
||||
|
||||
/**
|
||||
* True if the overflow menu is currently visible, false otherwise.
|
||||
*/
|
||||
_isOpen: boolean,
|
||||
|
||||
/**
|
||||
* Whether the recoding button should be enabled or not.
|
||||
*/
|
||||
_recordingEnabled: boolean,
|
||||
|
||||
/**
|
||||
* The width of the screen.
|
||||
*/
|
||||
_width: number,
|
||||
|
||||
/**
|
||||
* Whether or not the reactions feature is enabled.
|
||||
*/
|
||||
_reactionsEnabled: boolean,
|
||||
|
||||
/**
|
||||
* Used for hiding the dialog when the selection was completed.
|
||||
*/
|
||||
dispatch: Function,
|
||||
_isOpen: boolean;
|
||||
|
||||
/**
|
||||
* Whether or not speaker stats is disable.
|
||||
*/
|
||||
_isSpeakerStatsDisabled: boolean
|
||||
};
|
||||
|
||||
type State = {
|
||||
_isSpeakerStatsDisabled?: boolean;
|
||||
|
||||
/**
|
||||
* True if the bottom scheet is scrolled to the top.
|
||||
* Whether or not the reactions feature is enabled.
|
||||
*/
|
||||
scrolledToTop: boolean
|
||||
_reactionsEnabled: boolean;
|
||||
|
||||
/**
|
||||
* Whether the recoding button should be enabled or not.
|
||||
*/
|
||||
_recordingEnabled: boolean;
|
||||
|
||||
/**
|
||||
* The width of the screen.
|
||||
*/
|
||||
_width: number;
|
||||
|
||||
/**
|
||||
* Used for hiding the dialog when the selection was completed.
|
||||
*/
|
||||
dispatch: IStore['dispatch'];
|
||||
}
|
||||
|
||||
interface IState {
|
||||
|
||||
/**
|
||||
* True if the bottom sheet is scrolled to the top.
|
||||
*/
|
||||
scrolledToTop: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements a React {@code Component} with some extra actions in addition to
|
||||
* those in the toolbar.
|
||||
*/
|
||||
class OverflowMenu extends PureComponent<Props, State> {
|
||||
class OverflowMenu extends PureComponent<IProps, IState> {
|
||||
/**
|
||||
* Initializes a new {@code OverflowMenu} instance.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
@@ -105,7 +105,8 @@ class OverflowMenu extends PureComponent<Props, State> {
|
||||
const {
|
||||
_isSpeakerStatsDisabled,
|
||||
_reactionsEnabled,
|
||||
_width
|
||||
_width,
|
||||
dispatch
|
||||
} = this.props;
|
||||
const toolbarButtons = getMovableButtons(_width);
|
||||
|
||||
@@ -117,6 +118,7 @@ class OverflowMenu extends PureComponent<Props, State> {
|
||||
|
||||
const topButtonProps = {
|
||||
afterClick: this._onCancel,
|
||||
dispatch,
|
||||
showLabel: true,
|
||||
styles: {
|
||||
...bottomSheetStyles.buttons,
|
||||
@@ -132,21 +134,24 @@ class OverflowMenu extends PureComponent<Props, State> {
|
||||
<BottomSheet
|
||||
renderFooter = { _reactionsEnabled && !toolbarButtons.has('raisehand')
|
||||
? this._renderReactionMenu
|
||||
: null }>
|
||||
: undefined }>
|
||||
<OpenCarmodeButton { ...topButtonProps } />
|
||||
<AudioOnlyButton { ...buttonProps } />
|
||||
{!_reactionsEnabled && !toolbarButtons.has('raisehand') && <RaiseHandButton { ...buttonProps } />}
|
||||
<Divider style = { styles.divider } />
|
||||
{/* @ts-ignore */}
|
||||
<Divider style = { styles.divider as ViewStyle } />
|
||||
<SecurityDialogButton { ...buttonProps } />
|
||||
<RecordButton { ...buttonProps } />
|
||||
<LiveStreamButton { ...buttonProps } />
|
||||
<LinkToSalesforceButton { ...buttonProps } />
|
||||
<Divider style = { styles.divider } />
|
||||
{/* @ts-ignore */}
|
||||
<Divider style = { styles.divider as ViewStyle } />
|
||||
<SharedVideoButton { ...buttonProps } />
|
||||
{!toolbarButtons.has('screensharing') && <ScreenSharingButton { ...buttonProps } />}
|
||||
{!_isSpeakerStatsDisabled && <SpeakerStatsButton { ...buttonProps } />}
|
||||
{!toolbarButtons.has('tileview') && <TileViewButton { ...buttonProps } />}
|
||||
<Divider style = { styles.divider } />
|
||||
{/* @ts-ignore */}
|
||||
<Divider style = { styles.divider as ViewStyle } />
|
||||
<ClosedCaptionButton { ...buttonProps } />
|
||||
<SharedDocumentButton { ...buttonProps } />
|
||||
<SettingsButton { ...buttonProps } />
|
||||
@@ -165,7 +170,7 @@ class OverflowMenu extends PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Functoin to render the reaction menu as the footer of the bottom sheet.
|
||||
* Function to render the reaction menu as the footer of the bottom sheet.
|
||||
*
|
||||
* @returns {React$Element}
|
||||
*/
|
||||
@@ -181,9 +186,9 @@ class OverflowMenu extends PureComponent<Props, State> {
|
||||
*
|
||||
* @param {Object} state - Redux state.
|
||||
* @private
|
||||
* @returns {Props}
|
||||
* @returns {IProps}
|
||||
*/
|
||||
function _mapStateToProps(state) {
|
||||
function _mapStateToProps(state: IReduxState) {
|
||||
return {
|
||||
_isSpeakerStatsDisabled: isSpeakerStatsDisabled(state),
|
||||
_reactionsEnabled: isReactionsEnabled(state),
|
||||
@@ -1,5 +1,6 @@
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { openSheet } from '../../../base/dialog/actions';
|
||||
import { OVERFLOW_MENU_ENABLED } from '../../../base/flags/constants';
|
||||
import { getFeatureFlag } from '../../../base/flags/functions';
|
||||
@@ -9,21 +10,10 @@ import AbstractButton, { IProps as AbstractButtonProps } from '../../../base/too
|
||||
|
||||
import OverflowMenu from './OverflowMenu';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link OverflowMenuButton}.
|
||||
*/
|
||||
type Props = AbstractButtonProps & {
|
||||
|
||||
/**
|
||||
* The redux {@code dispatch} function.
|
||||
*/
|
||||
dispatch: Function
|
||||
};
|
||||
|
||||
/**
|
||||
* An implementation of a button for showing the {@code OverflowMenu}.
|
||||
*/
|
||||
class OverflowMenuButton extends AbstractButton<Props, *> {
|
||||
class OverflowMenuButton extends AbstractButton<AbstractButtonProps> {
|
||||
accessibilityLabel = 'toolbar.accessibilityLabel.moreActions';
|
||||
icon = IconDotsHorizontal;
|
||||
label = 'toolbar.moreActions';
|
||||
@@ -47,7 +37,7 @@ class OverflowMenuButton extends AbstractButton<Props, *> {
|
||||
* @private
|
||||
* @returns {Props}
|
||||
*/
|
||||
function _mapStateToProps(state): Object {
|
||||
function _mapStateToProps(state: IReduxState) {
|
||||
const enabledFlag = getFeatureFlag(state, OVERFLOW_MENU_ENABLED, true);
|
||||
|
||||
return {
|
||||
@@ -1,10 +1,8 @@
|
||||
// @flow
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
import { type Dispatch } from 'redux';
|
||||
|
||||
import { createToolbarEvent } from '../../../analytics/AnalyticsEvents';
|
||||
import { sendAnalytics } from '../../../analytics/functions';
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { RAISE_HAND_ENABLED } from '../../../base/flags/constants';
|
||||
import { getFeatureFlag } from '../../../base/flags/functions';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
@@ -14,33 +12,29 @@ import {
|
||||
getLocalParticipant,
|
||||
hasRaisedHand
|
||||
} from '../../../base/participants/functions';
|
||||
import { ILocalParticipant } from '../../../base/participants/types';
|
||||
import AbstractButton, { IProps as AbstractButtonProps } from '../../../base/toolbox/components/AbstractButton';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link RaiseHandButton}.
|
||||
*/
|
||||
type Props = AbstractButtonProps & {
|
||||
interface IProps extends AbstractButtonProps {
|
||||
|
||||
/**
|
||||
* The local participant.
|
||||
*/
|
||||
_localParticipant: Object,
|
||||
_localParticipant?: ILocalParticipant;
|
||||
|
||||
/**
|
||||
* Whether the participant raused their hand or not.
|
||||
* Whether the participant raised their hand or not.
|
||||
*/
|
||||
_raisedHand: boolean,
|
||||
|
||||
/**
|
||||
* The redux {@code dispatch} function.
|
||||
*/
|
||||
dispatch: Dispatch<any>
|
||||
};
|
||||
_raisedHand: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* An implementation of a button to raise or lower hand.
|
||||
*/
|
||||
class RaiseHandButton extends AbstractButton<Props, *> {
|
||||
class RaiseHandButton extends AbstractButton<IProps> {
|
||||
accessibilityLabel = 'toolbar.accessibilityLabel.raiseHand';
|
||||
icon = IconRaiseHand;
|
||||
label = 'toolbar.raiseYourHand';
|
||||
@@ -88,9 +82,9 @@ class RaiseHandButton extends AbstractButton<Props, *> {
|
||||
* @param {Object} state - The Redux state.
|
||||
* @param {Object} ownProps - The properties explicitly passed to the component instance.
|
||||
* @private
|
||||
* @returns {Props}
|
||||
* @returns {IProps}
|
||||
*/
|
||||
function _mapStateToProps(state, ownProps): Object {
|
||||
function _mapStateToProps(state: IReduxState, ownProps: any) {
|
||||
const _localParticipant = getLocalParticipant(state);
|
||||
const enabled = getFeatureFlag(state, RAISE_HAND_ENABLED, true);
|
||||
const { visible = enabled } = ownProps;
|
||||
@@ -1,7 +1,6 @@
|
||||
// @flow
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
import { IconCameraRefresh } from '../../../base/icons/svg';
|
||||
import { toggleCameraFacingMode } from '../../../base/media/actions';
|
||||
@@ -12,28 +11,23 @@ import { isLocalTrackMuted } from '../../../base/tracks/functions.native';
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link ToggleCameraButton}.
|
||||
*/
|
||||
type Props = AbstractButtonProps & {
|
||||
interface IProps extends AbstractButtonProps {
|
||||
|
||||
/**
|
||||
* Whether the current conference is in audio only mode or not.
|
||||
*/
|
||||
_audioOnly: boolean,
|
||||
_audioOnly: boolean;
|
||||
|
||||
/**
|
||||
* Whether video is currently muted or not.
|
||||
*/
|
||||
_videoMuted: boolean,
|
||||
|
||||
/**
|
||||
* The redux {@code dispatch} function.
|
||||
*/
|
||||
dispatch: Function
|
||||
};
|
||||
_videoMuted: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* An implementation of a button for toggling the camera facing mode.
|
||||
*/
|
||||
class ToggleCameraButton extends AbstractButton<Props, *> {
|
||||
class ToggleCameraButton extends AbstractButton<IProps> {
|
||||
accessibilityLabel = 'toolbar.accessibilityLabel.toggleCamera';
|
||||
icon = IconCameraRefresh;
|
||||
label = 'toolbar.toggleCamera';
|
||||
@@ -72,7 +66,7 @@ class ToggleCameraButton extends AbstractButton<Props, *> {
|
||||
* _videoMuted: boolean
|
||||
* }}
|
||||
*/
|
||||
function _mapStateToProps(state): Object {
|
||||
function _mapStateToProps(state: IReduxState) {
|
||||
const { enabled: audioOnly } = state['features/base/audio-only'];
|
||||
const tracks = state['features/base/tracks'];
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// @flow
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
import { IconAudioOnlyOff } from '../../../base/icons/svg';
|
||||
import { updateSettings } from '../../../base/settings/actions';
|
||||
@@ -10,23 +9,18 @@ import AbstractButton, { IProps as AbstractButtonProps } from '../../../base/too
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link ToggleSelfViewButton}.
|
||||
*/
|
||||
type Props = AbstractButtonProps & {
|
||||
interface IProps extends AbstractButtonProps {
|
||||
|
||||
/**
|
||||
* Whether the self view is disabled or not.
|
||||
*/
|
||||
_disableSelfView: boolean,
|
||||
|
||||
/**
|
||||
* The redux {@code dispatch} function.
|
||||
*/
|
||||
dispatch: Function
|
||||
};
|
||||
_disableSelfView: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* An implementation of a button for toggling the self view.
|
||||
*/
|
||||
class ToggleSelfViewButton extends AbstractButton<Props, *> {
|
||||
class ToggleSelfViewButton extends AbstractButton<IProps> {
|
||||
accessibilityLabel = 'toolbar.accessibilityLabel.selfView';
|
||||
icon = IconAudioOnlyOff;
|
||||
label = 'videothumbnail.hideSelfView';
|
||||
@@ -69,7 +63,7 @@ class ToggleSelfViewButton extends AbstractButton<Props, *> {
|
||||
* _disableSelfView: boolean
|
||||
* }}
|
||||
*/
|
||||
function _mapStateToProps(state): Object {
|
||||
function _mapStateToProps(state: IReduxState) {
|
||||
const { disableSelfView } = state['features/base/settings'];
|
||||
|
||||
return {
|
||||
@@ -1,13 +1,11 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { View, ViewStyle } from 'react-native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import ColorSchemeRegistry from '../../../base/color-scheme/ColorSchemeRegistry';
|
||||
import Platform from '../../../base/react/Platform.native';
|
||||
import { StyleType } from '../../../base/styles/functions.native';
|
||||
import ChatButton from '../../../chat/components/native/ChatButton';
|
||||
import ReactionsMenuButton from '../../../reactions/components/native/ReactionsMenuButton';
|
||||
import { isReactionsEnabled } from '../../../reactions/functions.any';
|
||||
@@ -27,38 +25,38 @@ import styles from './styles';
|
||||
/**
|
||||
* The type of {@link Toolbox}'s React {@code Component} props.
|
||||
*/
|
||||
type Props = {
|
||||
interface IProps {
|
||||
|
||||
/**
|
||||
* Whether the end conference feature is supported.
|
||||
*/
|
||||
_endConferenceSupported: boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the reactions feature is enabled.
|
||||
*/
|
||||
_reactionsEnabled: boolean,
|
||||
|
||||
/**
|
||||
* The color-schemed stylesheet of the feature.
|
||||
*/
|
||||
_styles: StyleType,
|
||||
|
||||
/**
|
||||
* The indicator which determines whether the toolbox is visible.
|
||||
*/
|
||||
_visible: boolean,
|
||||
_endConferenceSupported: boolean;
|
||||
|
||||
/**
|
||||
* Whether we are in visitors mode.
|
||||
*/
|
||||
_iAmVisitor: boolean,
|
||||
_iAmVisitor: boolean;
|
||||
|
||||
/**
|
||||
* Whether or not the reactions feature is enabled.
|
||||
*/
|
||||
_reactionsEnabled: boolean;
|
||||
|
||||
/**
|
||||
* The color-schemed stylesheet of the feature.
|
||||
*/
|
||||
_styles: any;
|
||||
|
||||
/**
|
||||
* The indicator which determines whether the toolbox is visible.
|
||||
*/
|
||||
_visible: boolean;
|
||||
|
||||
/**
|
||||
* The width of the screen.
|
||||
*/
|
||||
_width: number
|
||||
};
|
||||
_width: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the conference Toolbox on React Native.
|
||||
@@ -66,7 +64,7 @@ type Props = {
|
||||
* @param {Object} props - The props of the component.
|
||||
* @returns {React$Element}.
|
||||
*/
|
||||
function Toolbox(props: Props) {
|
||||
function Toolbox(props: IProps) {
|
||||
const { _endConferenceSupported, _reactionsEnabled, _styles, _visible, _iAmVisitor, _width } = props;
|
||||
|
||||
if (!_visible) {
|
||||
@@ -74,7 +72,7 @@ function Toolbox(props: Props) {
|
||||
}
|
||||
|
||||
const bottomEdge = Platform.OS === 'ios' && _visible;
|
||||
const { buttonStylesBorderless, hangupButtonStyles, hangupMenuButtonStyles, toggledButtonStyles } = _styles;
|
||||
const { buttonStylesBorderless, hangupButtonStyles, toggledButtonStyles } = _styles;
|
||||
const additionalButtons = getMovableButtons(_width);
|
||||
const backgroundToggledStyle = {
|
||||
...toggledButtonStyles,
|
||||
@@ -93,12 +91,14 @@ function Toolbox(props: Props) {
|
||||
|
||||
return (
|
||||
<View
|
||||
style = { styles.toolboxContainer }>
|
||||
style = { styles.toolboxContainer as ViewStyle }>
|
||||
<SafeAreaView
|
||||
accessibilityRole = 'toolbar'
|
||||
|
||||
// @ts-ignore
|
||||
edges = { [ bottomEdge && 'bottom' ].filter(Boolean) }
|
||||
pointerEvents = 'box-none'
|
||||
style = { style }>
|
||||
style = { style as ViewStyle }>
|
||||
{!_iAmVisitor && <AudioMuteButton
|
||||
styles = { buttonStylesBorderless }
|
||||
toggledStyles = { toggledButtonStyles } />
|
||||
@@ -108,9 +108,9 @@ function Toolbox(props: Props) {
|
||||
toggledStyles = { toggledButtonStyles } />
|
||||
}
|
||||
{additionalButtons.has('chat')
|
||||
&& <ChatButton
|
||||
styles = { buttonStylesBorderless }
|
||||
toggledStyles = { backgroundToggledStyle } />
|
||||
&& <ChatButton
|
||||
styles = { buttonStylesBorderless }
|
||||
toggledStyles = { backgroundToggledStyle } />
|
||||
}
|
||||
{!_iAmVisitor && additionalButtons.has('screensharing')
|
||||
&& <ScreenSharingButton styles = { buttonStylesBorderless } />}
|
||||
@@ -127,9 +127,7 @@ function Toolbox(props: Props) {
|
||||
toggledStyles = { toggledButtonStyles } />
|
||||
}
|
||||
{ _endConferenceSupported
|
||||
? <HangupMenuButton
|
||||
styles = { hangupMenuButtonStyles }
|
||||
toggledStyles = { toggledButtonStyles } />
|
||||
? <HangupMenuButton />
|
||||
: <HangupButton
|
||||
styles = { hangupButtonStyles } />
|
||||
}
|
||||
@@ -145,9 +143,9 @@ function Toolbox(props: Props) {
|
||||
* @param {Object} state - The redux state of which parts are to be mapped to
|
||||
* {@code Toolbox} props.
|
||||
* @private
|
||||
* @returns {Props}
|
||||
* @returns {IProps}
|
||||
*/
|
||||
function _mapStateToProps(state: Object): Object {
|
||||
function _mapStateToProps(state: IReduxState) {
|
||||
const { conference } = state['features/base/conference'];
|
||||
const endConferenceSupported = conference?.isEndConferenceSupported();
|
||||
|
||||
@@ -1,14 +1,25 @@
|
||||
import React, { ReactNode, useCallback } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import React, { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
import { createToolbarEvent } from '../../../analytics/AnalyticsEvents';
|
||||
import { sendAnalytics } from '../../../analytics/functions';
|
||||
import Popover from '../../../base/popover/components/Popover.web';
|
||||
import ContextMenu from '../../../base/ui/components/web/ContextMenu';
|
||||
import ContextMenuItemGroup from '../../../base/ui/components/web/ContextMenuItemGroup';
|
||||
import { setGifMenuVisibility } from '../../../gifs/actions';
|
||||
import { isGifsMenuOpen } from '../../../gifs/functions.web';
|
||||
import ReactionEmoji from '../../../reactions/components/web/ReactionEmoji';
|
||||
import ReactionsMenu from '../../../reactions/components/web/ReactionsMenu';
|
||||
import { REACTIONS_MENU_HEIGHT } from '../../../reactions/constants';
|
||||
import {
|
||||
GIFS_MENU_HEIGHT_IN_OVERFLOW_MENU,
|
||||
RAISE_HAND_ROW_HEIGHT,
|
||||
REACTIONS_MENU_HEIGHT_DRAWER,
|
||||
REACTIONS_MENU_HEIGHT_IN_OVERFLOW_MENU
|
||||
} from '../../../reactions/constants';
|
||||
import { getReactionsQueue } from '../../../reactions/functions.any';
|
||||
import { IReactionsMenuParent } from '../../../reactions/types';
|
||||
import { DRAWER_MAX_HEIGHT } from '../../constants';
|
||||
import { showOverflowDrawer } from '../../functions.web';
|
||||
|
||||
@@ -27,48 +38,85 @@ interface IProps {
|
||||
ariaControls: string;
|
||||
|
||||
/**
|
||||
* A child React Element to display within {@code Popover}.
|
||||
* Information about the buttons that need to be rendered in the overflow menu.
|
||||
*/
|
||||
children: ReactNode;
|
||||
buttons: Object[];
|
||||
|
||||
/**
|
||||
* Whether or not the OverflowMenu popover should display.
|
||||
*/
|
||||
isOpen: boolean;
|
||||
|
||||
/**
|
||||
* Esc key handler.
|
||||
*/
|
||||
onToolboxEscKey: (e?: React.KeyboardEvent) => void;
|
||||
|
||||
/**
|
||||
* Callback to change the visibility of the overflow menu.
|
||||
*/
|
||||
onVisibilityChange: Function;
|
||||
|
||||
/**
|
||||
* Whether or not to display the reactions in the mobile menu.
|
||||
* Whether to show the raise hand in the reactions menu or not.
|
||||
*/
|
||||
showMobileReactions: boolean;
|
||||
showRaiseHandInReactionsMenu: boolean;
|
||||
|
||||
/**
|
||||
* Whether or not to display the reactions menu.
|
||||
*/
|
||||
showReactionsMenu: boolean;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles()(() => {
|
||||
const useStyles = makeStyles<{ overflowDrawer: boolean; reactionsMenuHeight: number; }>()(
|
||||
(_theme, { reactionsMenuHeight, overflowDrawer }) => {
|
||||
return {
|
||||
overflowMenuDrawer: {
|
||||
overflowY: 'auto' as const,
|
||||
height: `calc(${DRAWER_MAX_HEIGHT} - ${REACTIONS_MENU_HEIGHT}px - 16px)`
|
||||
overflow: 'hidden',
|
||||
height: `calc(${DRAWER_MAX_HEIGHT} - ${reactionsMenuHeight}px - 16px)`
|
||||
},
|
||||
contextMenu: {
|
||||
position: 'relative' as const,
|
||||
right: 'auto',
|
||||
margin: 0,
|
||||
marginBottom: '8px',
|
||||
maxHeight: overflowDrawer ? undefined : 'calc(100vh - 100px)',
|
||||
paddingBottom: overflowDrawer ? undefined : 0,
|
||||
minWidth: '240px',
|
||||
overflow: 'hidden'
|
||||
},
|
||||
content: {
|
||||
position: 'relative',
|
||||
maxHeight: overflowDrawer
|
||||
? `calc(100% - ${reactionsMenuHeight}px - 16px)` : `calc(100vh - 100px - ${reactionsMenuHeight}px)`,
|
||||
overflowY: 'auto'
|
||||
},
|
||||
footer: {
|
||||
position: 'relative',
|
||||
bottom: 0
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const OverflowMenuButton = ({
|
||||
children,
|
||||
buttons,
|
||||
isOpen,
|
||||
onToolboxEscKey,
|
||||
onVisibilityChange,
|
||||
showMobileReactions
|
||||
showRaiseHandInReactionsMenu,
|
||||
showReactionsMenu
|
||||
}: IProps) => {
|
||||
const { classes } = useStyles();
|
||||
const overflowDrawer = useSelector(showOverflowDrawer);
|
||||
const reactionsQueue = useSelector(getReactionsQueue);
|
||||
const isGiphyVisible = useSelector(isGifsMenuOpen);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const onCloseDialog = useCallback(() => {
|
||||
onVisibilityChange(false);
|
||||
}, [ onVisibilityChange ]);
|
||||
if (isGiphyVisible && !overflowDrawer) {
|
||||
dispatch(setGifMenuVisibility(false));
|
||||
}
|
||||
}, [ onVisibilityChange, setGifMenuVisibility, isGiphyVisible, overflowDrawer, dispatch ]);
|
||||
|
||||
const onOpenDialog = useCallback(() => {
|
||||
onVisibilityChange(true);
|
||||
@@ -88,50 +136,118 @@ const OverflowMenuButton = ({
|
||||
onVisibilityChange(!isOpen);
|
||||
}, [ isOpen, onVisibilityChange ]);
|
||||
|
||||
const toolbarAccLabel = 'toolbar.accessibilityLabel.moreActionsMenu';
|
||||
const { t } = useTranslation();
|
||||
let reactionsMenuHeight = 0;
|
||||
|
||||
if (showReactionsMenu) {
|
||||
reactionsMenuHeight = REACTIONS_MENU_HEIGHT_DRAWER;
|
||||
if (!overflowDrawer) {
|
||||
reactionsMenuHeight = REACTIONS_MENU_HEIGHT_IN_OVERFLOW_MENU;
|
||||
}
|
||||
if (!showRaiseHandInReactionsMenu) {
|
||||
reactionsMenuHeight -= RAISE_HAND_ROW_HEIGHT;
|
||||
}
|
||||
if (!overflowDrawer && isGiphyVisible) {
|
||||
reactionsMenuHeight += GIFS_MENU_HEIGHT_IN_OVERFLOW_MENU;
|
||||
}
|
||||
}
|
||||
const { classes } = useStyles({
|
||||
reactionsMenuHeight,
|
||||
overflowDrawer
|
||||
});
|
||||
|
||||
const groupsJSX = buttons.map((buttonGroup: any) => (
|
||||
<ContextMenuItemGroup key = { `group-${buttonGroup[0].group}` }>
|
||||
{buttonGroup.map(({ key, Content, ...rest }: { Content: React.ElementType; key: string; }) => {
|
||||
const props: { buttonKey?: string; contextMenu?: boolean; showLabel?: boolean; } = { ...rest };
|
||||
|
||||
if (key !== 'reactions') {
|
||||
props.buttonKey = key;
|
||||
props.contextMenu = true;
|
||||
props.showLabel = true;
|
||||
}
|
||||
|
||||
return (
|
||||
<Content
|
||||
{ ...props }
|
||||
key = { key } />);
|
||||
})}
|
||||
</ContextMenuItemGroup>));
|
||||
|
||||
const overflowMenu = groupsJSX && (
|
||||
<ContextMenu
|
||||
accessibilityLabel = { t(toolbarAccLabel) }
|
||||
className = { classes.contextMenu }
|
||||
hidden = { false }
|
||||
id = 'overflow-context-menu'
|
||||
inDrawer = { overflowDrawer }
|
||||
onKeyDown = { onToolboxEscKey }>
|
||||
<div className = { classes.content }>
|
||||
{ groupsJSX }
|
||||
</div>
|
||||
{
|
||||
showReactionsMenu && (<div className = { classes.footer }>
|
||||
<ReactionsMenu
|
||||
parent = {
|
||||
overflowDrawer ? IReactionsMenuParent.OverflowDrawer : IReactionsMenuParent.OverflowMenu }
|
||||
showRaisedHand = { showRaiseHandInReactionsMenu } />
|
||||
</div>)
|
||||
}
|
||||
</ContextMenu>);
|
||||
|
||||
if (overflowDrawer) {
|
||||
return (
|
||||
<div className = 'toolbox-button-wth-dialog context-menu'>
|
||||
<>
|
||||
<OverflowToggleButton
|
||||
handleClick = { toggleDialogVisibility }
|
||||
isOpen = { isOpen }
|
||||
onKeyDown = { onEscClick } />
|
||||
<JitsiPortal>
|
||||
<Drawer
|
||||
isOpen = { isOpen }
|
||||
onClose = { onCloseDialog }>
|
||||
<>
|
||||
<div className = { classes.overflowMenuDrawer }>
|
||||
{ overflowMenu }
|
||||
</div>
|
||||
</>
|
||||
</Drawer>
|
||||
{showReactionsMenu && <div className = 'reactions-animations-container'>
|
||||
{reactionsQueue.map(({ reaction, uid }, index) => (<ReactionEmoji
|
||||
index = { index }
|
||||
key = { uid }
|
||||
reaction = { reaction }
|
||||
uid = { uid } />))}
|
||||
</div>}
|
||||
</JitsiPortal>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className = 'toolbox-button-wth-dialog context-menu'>
|
||||
{
|
||||
overflowDrawer ? (
|
||||
<>
|
||||
<OverflowToggleButton
|
||||
handleClick = { toggleDialogVisibility }
|
||||
isOpen = { isOpen }
|
||||
onKeyDown = { onEscClick } />
|
||||
<JitsiPortal>
|
||||
<Drawer
|
||||
isOpen = { isOpen }
|
||||
onClose = { onCloseDialog }>
|
||||
<>
|
||||
<div className = { classes.overflowMenuDrawer }>
|
||||
{children}
|
||||
</div>
|
||||
{showMobileReactions && <ReactionsMenu overflowMenu = { true } />}
|
||||
</>
|
||||
</Drawer>
|
||||
{showMobileReactions && <div className = 'reactions-animations-container'>
|
||||
{reactionsQueue.map(({ reaction, uid }, index) => (<ReactionEmoji
|
||||
index = { index }
|
||||
key = { uid }
|
||||
reaction = { reaction }
|
||||
uid = { uid } />))}
|
||||
</div>}
|
||||
</JitsiPortal>
|
||||
</>
|
||||
) : (
|
||||
<Popover
|
||||
content = { children }
|
||||
headingId = 'overflow-context-menu'
|
||||
onPopoverClose = { onCloseDialog }
|
||||
onPopoverOpen = { onOpenDialog }
|
||||
position = 'top'
|
||||
trigger = 'click'
|
||||
visible = { isOpen }>
|
||||
<OverflowToggleButton
|
||||
isOpen = { isOpen }
|
||||
onKeyDown = { onEscClick } />
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
<Popover
|
||||
content = { overflowMenu }
|
||||
headingId = 'overflow-context-menu'
|
||||
onPopoverClose = { onCloseDialog }
|
||||
onPopoverOpen = { onOpenDialog }
|
||||
position = 'top'
|
||||
trigger = 'click'
|
||||
visible = { isOpen }>
|
||||
<OverflowToggleButton
|
||||
isOpen = { isOpen }
|
||||
onKeyDown = { onEscClick } />
|
||||
</Popover>
|
||||
{showReactionsMenu && <div className = 'reactions-animations-container'>
|
||||
{reactionsQueue.map(({ reaction, uid }, index) => (<ReactionEmoji
|
||||
index = { index }
|
||||
key = { uid }
|
||||
reaction = { reaction }
|
||||
uid = { uid } />))}
|
||||
</div>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -30,7 +30,6 @@ import {
|
||||
import { getLocalVideoTrack } from '../../../base/tracks/functions.web';
|
||||
import { ITrack } from '../../../base/tracks/types';
|
||||
import ContextMenu from '../../../base/ui/components/web/ContextMenu';
|
||||
import ContextMenuItemGroup from '../../../base/ui/components/web/ContextMenuItemGroup';
|
||||
import { toggleChat } from '../../../chat/actions.web';
|
||||
import ChatButton from '../../../chat/components/web/ChatButton';
|
||||
import EmbedMeetingButton from '../../../embed-meeting/components/EmbedMeetingButton';
|
||||
@@ -54,9 +53,10 @@ import {
|
||||
addReactionToBuffer,
|
||||
toggleReactionsMenuVisibility
|
||||
} from '../../../reactions/actions.web';
|
||||
import RaiseHandButton from '../../../reactions/components/web/RaiseHandButton';
|
||||
import ReactionsMenuButton from '../../../reactions/components/web/ReactionsMenuButton';
|
||||
import { REACTIONS } from '../../../reactions/constants';
|
||||
import { isReactionsEnabled } from '../../../reactions/functions.web';
|
||||
import { isReactionsButtonEnabled, isReactionsEnabled } from '../../../reactions/functions.web';
|
||||
import LiveStreamButton from '../../../recording/components/LiveStream/web/LiveStreamButton';
|
||||
import RecordButton from '../../../recording/components/Recording/web/RecordButton';
|
||||
import { isSalesforceEnabled } from '../../../salesforce/functions';
|
||||
@@ -273,6 +273,11 @@ interface IProps extends WithTranslation {
|
||||
*/
|
||||
_raisedHand: boolean;
|
||||
|
||||
/**
|
||||
* Whether or not to display reactions in separate button.
|
||||
*/
|
||||
_reactionsButtonEnabled: boolean;
|
||||
|
||||
/**
|
||||
* Whether or not reactions feature is enabled.
|
||||
*/
|
||||
@@ -717,6 +722,7 @@ class Toolbox extends Component<IProps> {
|
||||
_isNarrowLayout,
|
||||
_isSpeakerStatsDisabled,
|
||||
_multiStreamModeEnabled,
|
||||
_reactionsButtonEnabled,
|
||||
_reactionsEnabled,
|
||||
_screenSharing,
|
||||
_shortcutsEnabled,
|
||||
@@ -755,8 +761,21 @@ class Toolbox extends Component<IProps> {
|
||||
group: 2
|
||||
};
|
||||
|
||||
const raisehand = (!_reactionsEnabled || (!_isNarrowLayout && !_isMobile)) && {
|
||||
// In Narrow layout and mobile web we are using drawer for popups and that is why it is better to include
|
||||
// all forms of reactions in the overflow menu. Otherwise the toolbox will be hidden and the reactions popup
|
||||
// misaligned.
|
||||
|
||||
const showReactionsAsPartOfRaiseHand
|
||||
= !_reactionsButtonEnabled && _reactionsEnabled && !_isNarrowLayout && !_isMobile;
|
||||
const raisehand = {
|
||||
key: 'raisehand',
|
||||
Content: showReactionsAsPartOfRaiseHand ? ReactionsMenuButton : RaiseHandButton,
|
||||
handleClick: this._onToolbarToggleRaiseHand,
|
||||
group: 2
|
||||
};
|
||||
|
||||
const reactions = _reactionsButtonEnabled && _reactionsEnabled && {
|
||||
key: 'reactions',
|
||||
Content: ReactionsMenuButton,
|
||||
handleClick: this._onToolbarToggleRaiseHand,
|
||||
group: 2
|
||||
@@ -931,6 +950,7 @@ class Toolbox extends Component<IProps> {
|
||||
desktop,
|
||||
chat,
|
||||
raisehand,
|
||||
reactions,
|
||||
participants,
|
||||
invite,
|
||||
tileview,
|
||||
@@ -1015,9 +1035,8 @@ class Toolbox extends Component<IProps> {
|
||||
|
||||
this._setButtonsNotifyClickMode(buttons);
|
||||
const isHangupVisible = isToolbarButtonEnabled('hangup', _toolbarButtons);
|
||||
const { order } = THRESHOLDS.find(({ width }) => _clientWidth > width)
|
||||
let { order } = THRESHOLDS.find(({ width }) => _clientWidth > width)
|
||||
|| THRESHOLDS[THRESHOLDS.length - 1];
|
||||
let sliceIndex = order.length + 2;
|
||||
|
||||
const keys = Object.keys(buttons);
|
||||
|
||||
@@ -1028,6 +1047,11 @@ class Toolbox extends Component<IProps> {
|
||||
!_jwtDisabledButons.includes(key)
|
||||
&& (isToolbarButtonEnabled(key, _toolbarButtons) || isToolbarButtonEnabled(alias, _toolbarButtons))
|
||||
);
|
||||
const filteredKeys = filtered.map(button => button.key);
|
||||
|
||||
order = order.filter(key => filteredKeys.includes(buttons[key as keyof typeof buttons].key));
|
||||
|
||||
let sliceIndex = order.length + 2;
|
||||
|
||||
if (isHangupVisible) {
|
||||
sliceIndex -= 1;
|
||||
@@ -1412,6 +1436,7 @@ class Toolbox extends Component<IProps> {
|
||||
_overflowDrawer,
|
||||
_overflowMenuVisible,
|
||||
_reactionsEnabled,
|
||||
_reactionsButtonEnabled,
|
||||
_toolbarButtons,
|
||||
classes,
|
||||
t
|
||||
@@ -1421,6 +1446,12 @@ class Toolbox extends Component<IProps> {
|
||||
const containerClassName = `toolbox-content${_isMobile || _isNarrowLayout ? ' toolbox-content-mobile' : ''}`;
|
||||
|
||||
const { mainMenuButtons, overflowMenuButtons } = this._getVisibleButtons();
|
||||
const raiseHandInOverflowMenu = overflowMenuButtons.some(({ key }) => key === 'raisehand');
|
||||
const showReactionsInOverflowMenu
|
||||
= (_reactionsEnabled && !_reactionsButtonEnabled
|
||||
&& (raiseHandInOverflowMenu || _isNarrowLayout || _isMobile))
|
||||
|| overflowMenuButtons.some(({ key }) => key === 'reactions');
|
||||
const showRaiseHandInReactionsMenu = showReactionsInOverflowMenu && raiseHandInOverflowMenu;
|
||||
|
||||
return (
|
||||
<div className = { containerClassName }>
|
||||
@@ -1444,47 +1475,36 @@ class Toolbox extends Component<IProps> {
|
||||
{Boolean(overflowMenuButtons.length) && (
|
||||
<OverflowMenuButton
|
||||
ariaControls = 'overflow-menu'
|
||||
isOpen = { _overflowMenuVisible }
|
||||
key = 'overflow-menu'
|
||||
onVisibilityChange = { this._onSetOverflowVisible }
|
||||
showMobileReactions = {
|
||||
_reactionsEnabled && (_isMobile || _isNarrowLayout)
|
||||
}>
|
||||
<ContextMenu
|
||||
accessibilityLabel = { t(toolbarAccLabel) }
|
||||
className = { classes.contextMenu }
|
||||
hidden = { false }
|
||||
id = 'overflow-context-menu'
|
||||
inDrawer = { _overflowDrawer }
|
||||
onKeyDown = { this._onEscKey }>
|
||||
{overflowMenuButtons.reduce((acc, val) => {
|
||||
if (acc.length) {
|
||||
const prev = acc[acc.length - 1];
|
||||
const group = prev[prev.length - 1].group;
|
||||
buttons = { overflowMenuButtons.reduce((acc, val) => {
|
||||
if (val.key === 'reactions' && showReactionsInOverflowMenu) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
if (group === val.group) {
|
||||
prev.push(val);
|
||||
} else {
|
||||
acc.push([ val ]);
|
||||
}
|
||||
if (val.key === 'raisehand' && showRaiseHandInReactionsMenu) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
if (acc.length) {
|
||||
const prev = acc[acc.length - 1];
|
||||
const group = prev[prev.length - 1].group;
|
||||
|
||||
if (group === val.group) {
|
||||
prev.push(val);
|
||||
} else {
|
||||
acc.push([ val ]);
|
||||
}
|
||||
} else {
|
||||
acc.push([ val ]);
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []).map((buttonGroup: any) => (
|
||||
<ContextMenuItemGroup key = { `group-${buttonGroup[0].group}` }>
|
||||
{buttonGroup.map(({ key, Content, ...rest }: any) => (
|
||||
key !== 'raisehand' || !_reactionsEnabled)
|
||||
&& <Content
|
||||
{ ...rest }
|
||||
buttonKey = { key }
|
||||
contextMenu = { true }
|
||||
key = { key }
|
||||
showLabel = { true } />)}
|
||||
</ContextMenuItemGroup>))}
|
||||
</ContextMenu>
|
||||
</OverflowMenuButton>
|
||||
return acc;
|
||||
}, []) }
|
||||
isOpen = { _overflowMenuVisible }
|
||||
key = 'overflow-menu'
|
||||
onToolboxEscKey = { this._onEscKey }
|
||||
onVisibilityChange = { this._onSetOverflowVisible }
|
||||
showRaiseHandInReactionsMenu = { showRaiseHandInReactionsMenu }
|
||||
showReactionsMenu = { showReactionsInOverflowMenu } />
|
||||
)}
|
||||
|
||||
{ isToolbarButtonEnabled('hangup', _toolbarButtons) && (
|
||||
@@ -1554,6 +1574,7 @@ function _mapStateToProps(state: IReduxState, ownProps: any) {
|
||||
const localVideo = getLocalVideoTrack(state['features/base/tracks']);
|
||||
const { clientWidth } = state['features/base/responsive-ui'];
|
||||
let toolbarButtons = ownProps.toolbarButtons || getToolbarButtons(state);
|
||||
const _reactionsEnabled = isReactionsEnabled(state);
|
||||
|
||||
if (iAmVisitor(state)) {
|
||||
toolbarButtons = VISITORS_MODE_BUTTONS.filter(e => toolbarButtons.indexOf(e) > -1);
|
||||
@@ -1590,7 +1611,8 @@ function _mapStateToProps(state: IReduxState, ownProps: any) {
|
||||
_overflowDrawer: overflowDrawer,
|
||||
_participantsPaneOpen: getParticipantsPaneOpen(state),
|
||||
_raisedHand: hasRaisedHand(localParticipant),
|
||||
_reactionsEnabled: isReactionsEnabled(state),
|
||||
_reactionsButtonEnabled: isReactionsButtonEnabled(state),
|
||||
_reactionsEnabled,
|
||||
_screenSharing: isScreenVideoShared(state),
|
||||
_shortcutsEnabled: areKeyboardShortcutsEnabled(state),
|
||||
_tileViewEnabled: shouldDisplayTileView(state),
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
* Thresholds for displaying toolbox buttons.
|
||||
*/
|
||||
export const THRESHOLDS = [
|
||||
{
|
||||
width: 565,
|
||||
order: [ 'microphone', 'camera', 'desktop', 'chat', 'raisehand', 'reactions', 'participants', 'tileview' ]
|
||||
},
|
||||
{
|
||||
width: 520,
|
||||
order: [ 'microphone', 'camera', 'desktop', 'chat', 'raisehand', 'participants', 'tileview' ]
|
||||
|
||||
@@ -124,6 +124,16 @@ export function showOverflowDrawer(state: IReduxState) {
|
||||
return state['features/toolbox'].overflowDrawer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the overflow menu button is displayed and false otherwise.
|
||||
*
|
||||
* @param {IReduxState} state - The state from the Redux store.
|
||||
* @returns {boolean} - True if the overflow menu button is displayed and false otherwise.
|
||||
*/
|
||||
export function showOverflowMenu(state: IReduxState) {
|
||||
return state['features/toolbox'].overflowMenuVisible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the toolbox is enabled or not.
|
||||
*
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
// @flow
|
||||
import { WithTranslation } from 'react-i18next';
|
||||
|
||||
import { translate } from '../../base/i18n/functions';
|
||||
import ExpandedLabel, { Props as AbstractProps } from '../../base/label/components/native/ExpandedLabel';
|
||||
import ExpandedLabel, { IProps as AbstractProps } from '../../base/label/components/native/ExpandedLabel';
|
||||
|
||||
type Props = AbstractProps & {
|
||||
t: Function
|
||||
}
|
||||
type Props = AbstractProps & WithTranslation;
|
||||
|
||||
/**
|
||||
* A react {@code Component} that implements an expanded label as tooltip-like
|
||||
@@ -1,12 +1,10 @@
|
||||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { translate } from '../../base/i18n/functions';
|
||||
import Label from '../../base/label/components/native/Label';
|
||||
|
||||
import { type Props, _mapStateToProps } from './AbstractTranscribingLabel';
|
||||
import { IProps, _mapStateToProps } from './AbstractTranscribingLabel';
|
||||
|
||||
/**
|
||||
* React {@code Component} for displaying a label when a transcriber is in the
|
||||
@@ -14,7 +12,7 @@ import { type Props, _mapStateToProps } from './AbstractTranscribingLabel';
|
||||
*
|
||||
* @augments Component
|
||||
*/
|
||||
class TranscribingLabel extends Component<Props> {
|
||||
class TranscribingLabel extends Component<IProps> {
|
||||
|
||||
/**
|
||||
* Renders the platform-specific label component.
|
||||
@@ -1,7 +1,6 @@
|
||||
// @flow
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { approveParticipant } from '../../../av-moderation/actions';
|
||||
import { isSupported } from '../../../av-moderation/functions';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
@@ -11,34 +10,29 @@ import { getParticipantById, isLocalParticipantModerator } from '../../../base/p
|
||||
import AbstractButton, { IProps as AbstractButtonProps } from '../../../base/toolbox/components/AbstractButton';
|
||||
import { isForceMuted } from '../../../participants-pane/functions';
|
||||
|
||||
export type Props = AbstractButtonProps & {
|
||||
|
||||
/**
|
||||
* The redux {@code dispatch} function.
|
||||
*/
|
||||
dispatch: Function,
|
||||
export interface IProps extends AbstractButtonProps {
|
||||
|
||||
/**
|
||||
* Whether or not the participant is audio force muted.
|
||||
*/
|
||||
isAudioForceMuted: boolean,
|
||||
isAudioForceMuted: boolean;
|
||||
|
||||
/**
|
||||
* Whether or not the participant is video force muted.
|
||||
*/
|
||||
isVideoForceMuted: boolean,
|
||||
isVideoForceMuted: boolean;
|
||||
|
||||
/**
|
||||
* The ID of the participant object that this button is supposed to
|
||||
* ask to unmute.
|
||||
*/
|
||||
participantID: string
|
||||
};
|
||||
participantID: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* An abstract remote video menu button which asks the remote participant to unmute.
|
||||
*/
|
||||
class AskUnmuteButton extends AbstractButton<Props, *> {
|
||||
class AskUnmuteButton extends AbstractButton<IProps> {
|
||||
accessibilityLabel = 'participantsPane.actions.askUnmute';
|
||||
icon = IconMic;
|
||||
label = 'participantsPane.actions.askUnmute';
|
||||
@@ -91,16 +85,16 @@ class AskUnmuteButton extends AbstractButton<Props, *> {
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @param {Object} ownProps - Properties of component.
|
||||
* @returns {Props}
|
||||
* @returns {IProps}
|
||||
*/
|
||||
function mapStateToProps(state, ownProps) {
|
||||
function mapStateToProps(state: IReduxState, ownProps: any) {
|
||||
const { participantID } = ownProps;
|
||||
const participant = getParticipantById(state, participantID);
|
||||
|
||||
return {
|
||||
isAudioForceMuted: isForceMuted(participant, MEDIA_TYPE.AUDIO, state),
|
||||
isVideoForceMuted: isForceMuted(participant, MEDIA_TYPE.VIDEO, state),
|
||||
visible: isLocalParticipantModerator(state) && isSupported()
|
||||
visible: isLocalParticipantModerator(state) && isSupported()(state)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,28 +7,18 @@ import AbstractButton, { IProps as AbstractButtonProps } from '../../../base/too
|
||||
|
||||
import ConnectionStatusComponent from './ConnectionStatusComponent';
|
||||
|
||||
export type Props = AbstractButtonProps & {
|
||||
|
||||
/**
|
||||
* The redux {@code dispatch} function.
|
||||
*/
|
||||
dispatch: Function,
|
||||
export interface IProps extends AbstractButtonProps {
|
||||
|
||||
/**
|
||||
* The ID of the participant that this button is supposed to pin.
|
||||
*/
|
||||
participantID: string,
|
||||
|
||||
/**
|
||||
* The function to be used to translate i18n labels.
|
||||
*/
|
||||
t: Function
|
||||
};
|
||||
participantID: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A remote video menu button which shows the connection statistics.
|
||||
*/
|
||||
class ConnectionStatusButton extends AbstractButton<Props, *> {
|
||||
class ConnectionStatusButton extends AbstractButton<IProps> {
|
||||
icon = IconInfoCircle;
|
||||
label = 'videothumbnail.connectionInfo';
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
@@ -28,8 +26,6 @@ class GrantModeratorDialog extends AbstractGrantModeratorDialog {
|
||||
onSubmit = { this._onSubmit } />
|
||||
);
|
||||
}
|
||||
|
||||
_onSubmit: () => boolean;
|
||||
}
|
||||
|
||||
export default translate(connect(abstractMapStateToProps)(GrantModeratorDialog));
|
||||
@@ -1,7 +1,6 @@
|
||||
// @flow
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
import { isLocalParticipantModerator } from '../../../base/participants/functions';
|
||||
import AbstractKickButton from '../AbstractKickButton';
|
||||
@@ -12,7 +11,7 @@ import AbstractKickButton from '../AbstractKickButton';
|
||||
* @param {Object} state - The Redux state.
|
||||
* @returns {Props}
|
||||
*/
|
||||
function _mapStateToProps(state) {
|
||||
function _mapStateToProps(state: IReduxState) {
|
||||
return {
|
||||
visible: isLocalParticipantModerator(state)
|
||||
};
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
@@ -29,8 +27,6 @@ class KickRemoteParticipantDialog extends AbstractKickRemoteParticipantDialog {
|
||||
title = 'dialog.kickParticipantTitle' />
|
||||
);
|
||||
}
|
||||
|
||||
_onSubmit: () => boolean;
|
||||
}
|
||||
|
||||
export default translate(connect()(KickRemoteParticipantDialog));
|
||||
@@ -1,7 +1,8 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
import { Text, TextStyle, View, ViewStyle } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState, IStore } from '../../../app/types';
|
||||
import Avatar from '../../../base/avatar/components/Avatar';
|
||||
import BottomSheet from '../../../base/dialog/components/native/BottomSheet';
|
||||
import { bottomSheetStyles } from '../../../base/dialog/components/native/styles';
|
||||
@@ -10,50 +11,50 @@ import {
|
||||
getLocalParticipant,
|
||||
getParticipantDisplayName
|
||||
} from '../../../base/participants/functions';
|
||||
import { ILocalParticipant } from '../../../base/participants/types';
|
||||
import ToggleSelfViewButton from '../../../toolbox/components/native/ToggleSelfViewButton';
|
||||
|
||||
import ConnectionStatusButton from './ConnectionStatusButton';
|
||||
import styles from './styles';
|
||||
|
||||
|
||||
/**
|
||||
* Size of the rendered avatar in the menu.
|
||||
*/
|
||||
const AVATAR_SIZE = 24;
|
||||
|
||||
type Props = {
|
||||
interface IProps {
|
||||
|
||||
/**
|
||||
* The local participant.
|
||||
*/
|
||||
_participant: Object,
|
||||
_participant?: ILocalParticipant;
|
||||
|
||||
/**
|
||||
* Display name of the participant retrieved from Redux.
|
||||
*/
|
||||
_participantDisplayName: string,
|
||||
_participantDisplayName: string;
|
||||
|
||||
/**
|
||||
* The Redux dispatch function.
|
||||
*/
|
||||
dispatch: Function,
|
||||
dispatch: IStore['dispatch'];
|
||||
|
||||
/**
|
||||
* Translation function.
|
||||
*/
|
||||
t: Function
|
||||
t: Function;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class to implement a popup menu that opens upon long pressing a thumbnail.
|
||||
*/
|
||||
class LocalVideoMenu extends PureComponent<Props> {
|
||||
class LocalVideoMenu extends PureComponent<IProps> {
|
||||
/**
|
||||
* Constructor of the component.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this._renderMenuHeader = this._renderMenuHeader.bind(this);
|
||||
@@ -68,7 +69,7 @@ class LocalVideoMenu extends PureComponent<Props> {
|
||||
const { _participant } = this.props;
|
||||
const buttonProps = {
|
||||
showLabel: true,
|
||||
participantID: _participant.id,
|
||||
participantID: _participant?.id ?? '',
|
||||
styles: bottomSheetStyles.buttons
|
||||
};
|
||||
|
||||
@@ -94,11 +95,11 @@ class LocalVideoMenu extends PureComponent<Props> {
|
||||
<View
|
||||
style = { [
|
||||
bottomSheetStyles.sheet,
|
||||
styles.participantNameContainer ] }>
|
||||
styles.participantNameContainer ] as ViewStyle[] }>
|
||||
<Avatar
|
||||
participantId = { _participant.id }
|
||||
participantId = { _participant?.id }
|
||||
size = { AVATAR_SIZE } />
|
||||
<Text style = { styles.participantNameLabel }>
|
||||
<Text style = { styles.participantNameLabel as TextStyle }>
|
||||
{ this.props._participantDisplayName }
|
||||
</Text>
|
||||
</View>
|
||||
@@ -111,14 +112,14 @@ class LocalVideoMenu extends PureComponent<Props> {
|
||||
*
|
||||
* @param {Object} state - Redux state.
|
||||
* @private
|
||||
* @returns {Props}
|
||||
* @returns {IProps}
|
||||
*/
|
||||
function _mapStateToProps(state) {
|
||||
function _mapStateToProps(state: IReduxState) {
|
||||
const participant = getLocalParticipant(state);
|
||||
|
||||
return {
|
||||
_participant: participant,
|
||||
_participantDisplayName: getParticipantDisplayName(state, participant.id)
|
||||
_participantDisplayName: getParticipantDisplayName(state, participant?.id ?? '')
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// @flow
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
import { isLocalParticipantModerator } from '../../../base/participants/functions';
|
||||
import AbstractMuteButton, { _mapStateToProps as _abstractMapStateToProps } from '../AbstractMuteButton';
|
||||
@@ -13,7 +12,7 @@ import AbstractMuteButton, { _mapStateToProps as _abstractMapStateToProps } from
|
||||
* @param {Object} ownProps - Properties of component.
|
||||
* @returns {Props}
|
||||
*/
|
||||
function _mapStateToProps(state, ownProps) {
|
||||
function _mapStateToProps(state: IReduxState, ownProps: any) {
|
||||
return {
|
||||
..._abstractMapStateToProps(state, ownProps),
|
||||
visible: isLocalParticipantModerator(state)
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import { ViewStyle } from 'react-native';
|
||||
import Dialog from 'react-native-dialog';
|
||||
import { Divider } from 'react-native-paper';
|
||||
import { connect } from 'react-redux';
|
||||
@@ -46,7 +47,8 @@ class MuteEveryoneDialog extends AbstractMuteEveryoneDialog<IProps> {
|
||||
descriptionKey = { this.state.content }
|
||||
onSubmit = { this._onSubmit }
|
||||
title = { this.props.title } >
|
||||
<Divider style = { styles.dividerDialog } />
|
||||
{/* @ts-ignore */}
|
||||
<Divider style = { styles.dividerDialog as ViewStyle } />
|
||||
{ this._renderSwitch() }
|
||||
</ConfirmDialog>
|
||||
);
|
||||
@@ -1,7 +1,6 @@
|
||||
// @flow
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
import { isLocalParticipantModerator } from '../../../base/participants/functions';
|
||||
import AbstractMuteEveryoneElseButton from '../AbstractMuteEveryoneElseButton';
|
||||
@@ -12,7 +11,7 @@ import AbstractMuteEveryoneElseButton from '../AbstractMuteEveryoneElseButton';
|
||||
* @param {Object} state - The Redux state.
|
||||
* @returns {Props}
|
||||
*/
|
||||
function _mapStateToProps(state) {
|
||||
function _mapStateToProps(state: IReduxState) {
|
||||
return {
|
||||
visible: isLocalParticipantModerator(state)
|
||||
};
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
@@ -29,8 +27,6 @@ class MuteRemoteParticipantsVideoDialog extends AbstractMuteRemoteParticipantsVi
|
||||
onSubmit = { this._onSubmit } />
|
||||
);
|
||||
}
|
||||
|
||||
_onSubmit: () => boolean;
|
||||
}
|
||||
|
||||
export default translate(connect(abstractMapStateToProps)(MuteRemoteParticipantsVideoDialog));
|
||||
@@ -1,7 +1,6 @@
|
||||
// @flow
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
import { isLocalParticipantModerator } from '../../../base/participants/functions';
|
||||
import AbstractMuteVideoButton, { _mapStateToProps as _abstractMapStateToProps } from '../AbstractMuteVideoButton';
|
||||
@@ -13,7 +12,7 @@ import AbstractMuteVideoButton, { _mapStateToProps as _abstractMapStateToProps }
|
||||
* @param {Object} ownProps - Properties of component.
|
||||
* @returns {Props}
|
||||
*/
|
||||
function _mapStateToProps(state, ownProps) {
|
||||
function _mapStateToProps(state: IReduxState, ownProps: any) {
|
||||
return {
|
||||
..._abstractMapStateToProps(state, ownProps),
|
||||
visible: isLocalParticipantModerator(state)
|
||||
@@ -1,40 +1,29 @@
|
||||
// @flow
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
import { IconEnlarge } from '../../../base/icons/svg';
|
||||
import { pinParticipant } from '../../../base/participants/actions';
|
||||
import AbstractButton, { IProps as AbstractButtonProps } from '../../../base/toolbox/components/AbstractButton';
|
||||
import { shouldDisplayTileView } from '../../../video-layout/functions';
|
||||
|
||||
export type Props = AbstractButtonProps & {
|
||||
export interface IProps extends AbstractButtonProps {
|
||||
|
||||
/**
|
||||
* True if tile view is currently enabled.
|
||||
*/
|
||||
_tileViewEnabled: boolean,
|
||||
|
||||
/**
|
||||
* The redux {@code dispatch} function.
|
||||
*/
|
||||
dispatch: Function,
|
||||
_tileViewEnabled?: boolean;
|
||||
|
||||
/**
|
||||
* The ID of the participant that this button is supposed to pin.
|
||||
*/
|
||||
participantID: string,
|
||||
|
||||
/**
|
||||
* The function to be used to translate i18n labels.
|
||||
*/
|
||||
t: Function
|
||||
};
|
||||
participantID: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A remote video menu button which pins a participant and exist the tile view.
|
||||
*/
|
||||
class PinButton extends AbstractButton<Props, *> {
|
||||
class PinButton extends AbstractButton<IProps> {
|
||||
accessibilityLabel = 'toolbar.accessibilityLabel.show';
|
||||
icon = IconEnlarge;
|
||||
label = 'videothumbnail.show';
|
||||
@@ -57,9 +46,9 @@ class PinButton extends AbstractButton<Props, *> {
|
||||
* Maps part of the Redux state to the props of this component.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @returns {Props}
|
||||
* @returns {IProps}
|
||||
*/
|
||||
function _mapStateToProps(state) {
|
||||
function _mapStateToProps(state: IReduxState) {
|
||||
const { isOpen } = state['features/participants-pane'];
|
||||
|
||||
return {
|
||||
@@ -1,8 +1,9 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
import { Text, TextStyle, View, ViewStyle } from 'react-native';
|
||||
import { Divider } from 'react-native-paper';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import Avatar from '../../../base/avatar/components/Avatar';
|
||||
import { hideSheet } from '../../../base/dialog/actions';
|
||||
import BottomSheet from '../../../base/dialog/components/native/BottomSheet';
|
||||
@@ -16,10 +17,11 @@ import {
|
||||
isLocalParticipantModerator
|
||||
} from '../../../base/participants/functions';
|
||||
import { getBreakoutRooms, getCurrentRoomId } from '../../../breakout-rooms/functions';
|
||||
import { IRoom } from '../../../breakout-rooms/types';
|
||||
import PrivateMessageButton from '../../../chat/components/native/PrivateMessageButton';
|
||||
import ConnectionStatusButton from '../native/ConnectionStatusButton';
|
||||
|
||||
import AskUnmuteButton from './AskUnmuteButton';
|
||||
import ConnectionStatusButton from './ConnectionStatusButton';
|
||||
import GrantModeratorButton from './GrantModeratorButton';
|
||||
import KickButton from './KickButton';
|
||||
import MuteButton from './MuteButton';
|
||||
@@ -31,85 +33,84 @@ import styles from './styles';
|
||||
|
||||
// import VolumeSlider from './VolumeSlider';
|
||||
|
||||
|
||||
/**
|
||||
* Size of the rendered avatar in the menu.
|
||||
*/
|
||||
const AVATAR_SIZE = 24;
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The Redux dispatch function.
|
||||
*/
|
||||
dispatch: Function,
|
||||
|
||||
/**
|
||||
* The ID of the participant for which this menu opened for.
|
||||
*/
|
||||
participantId: String,
|
||||
interface IProps {
|
||||
|
||||
/**
|
||||
* The id of the current room.
|
||||
*/
|
||||
_currentRoomId: String,
|
||||
|
||||
/**
|
||||
* Whether or not to display the kick button.
|
||||
*/
|
||||
_disableKick: boolean,
|
||||
|
||||
/**
|
||||
* Whether or not to display the send private message button.
|
||||
*/
|
||||
_disablePrivateChat: Boolean,
|
||||
|
||||
/**
|
||||
* Whether or not to display the remote mute buttons.
|
||||
*/
|
||||
_disableRemoteMute: boolean,
|
||||
_currentRoomId: string;
|
||||
|
||||
/**
|
||||
* Whether or not to display the grant moderator button.
|
||||
*/
|
||||
_disableGrantModerator: Boolean,
|
||||
_disableGrantModerator: boolean;
|
||||
|
||||
/**
|
||||
* Whether or not to display the kick button.
|
||||
*/
|
||||
_disableKick: boolean;
|
||||
|
||||
/**
|
||||
* Whether or not to display the send private message button.
|
||||
*/
|
||||
_disablePrivateChat: boolean;
|
||||
|
||||
/**
|
||||
* Whether or not to display the remote mute buttons.
|
||||
*/
|
||||
_disableRemoteMute: boolean;
|
||||
|
||||
/**
|
||||
* Whether the participant is present in the room or not.
|
||||
*/
|
||||
_isParticipantAvailable?: boolean,
|
||||
_isParticipantAvailable?: boolean;
|
||||
|
||||
/**
|
||||
* Whether the local participant is moderator or not.
|
||||
*/
|
||||
_moderator: boolean,
|
||||
_moderator: boolean;
|
||||
|
||||
/**
|
||||
* Display name of the participant retrieved from Redux.
|
||||
*/
|
||||
_participantDisplayName: string,
|
||||
_participantDisplayName: string;
|
||||
|
||||
/**
|
||||
* Array containing the breakout rooms.
|
||||
*/
|
||||
_rooms: Array<Object>,
|
||||
_rooms: Array<IRoom>;
|
||||
|
||||
/**
|
||||
* The Redux dispatch function.
|
||||
*/
|
||||
dispatch: Function;
|
||||
|
||||
/**
|
||||
* The ID of the participant for which this menu opened for.
|
||||
*/
|
||||
participantId: string;
|
||||
|
||||
/**
|
||||
* Translation function.
|
||||
*/
|
||||
t: Function
|
||||
t: Function;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class to implement a popup menu that opens upon long pressing a thumbnail.
|
||||
*/
|
||||
class RemoteVideoMenu extends PureComponent<Props> {
|
||||
class RemoteVideoMenu extends PureComponent<IProps> {
|
||||
/**
|
||||
* Constructor of the component.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this._onCancel = this._onCancel.bind(this);
|
||||
@@ -149,7 +150,8 @@ class RemoteVideoMenu extends PureComponent<Props> {
|
||||
{ !_disableRemoteMute && <MuteButton { ...buttonProps } /> }
|
||||
<MuteEveryoneElseButton { ...buttonProps } />
|
||||
{ !_disableRemoteMute && <MuteVideoButton { ...buttonProps } /> }
|
||||
<Divider style = { styles.divider } />
|
||||
{/* @ts-ignore */}
|
||||
<Divider style = { styles.divider as ViewStyle } />
|
||||
{ !_disableKick && <KickButton { ...buttonProps } /> }
|
||||
{ !_disableGrantModerator && <GrantModeratorButton { ...buttonProps } /> }
|
||||
<PinButton { ...buttonProps } />
|
||||
@@ -158,9 +160,10 @@ class RemoteVideoMenu extends PureComponent<Props> {
|
||||
{ ...buttonProps }
|
||||
afterClick = { undefined } />
|
||||
{_moderator && _rooms.length > 1 && <>
|
||||
<Divider style = { styles.divider } />
|
||||
<View style = { styles.contextMenuItem }>
|
||||
<Text style = { styles.contextMenuItemText }>
|
||||
{/* @ts-ignore */}
|
||||
<Divider style = { styles.divider as ViewStyle } />
|
||||
<View style = { styles.contextMenuItem as ViewStyle }>
|
||||
<Text style = { styles.contextMenuItemText as TextStyle }>
|
||||
{t('breakoutRooms.actions.sendToBreakoutRoom')}
|
||||
</Text>
|
||||
</View>
|
||||
@@ -196,11 +199,11 @@ class RemoteVideoMenu extends PureComponent<Props> {
|
||||
<View
|
||||
style = { [
|
||||
bottomSheetStyles.sheet,
|
||||
styles.participantNameContainer ] }>
|
||||
styles.participantNameContainer ] as ViewStyle[] }>
|
||||
<Avatar
|
||||
participantId = { participantId }
|
||||
size = { AVATAR_SIZE } />
|
||||
<Text style = { styles.participantNameLabel }>
|
||||
<Text style = { styles.participantNameLabel as TextStyle }>
|
||||
{ this.props._participantDisplayName }
|
||||
</Text>
|
||||
</View>
|
||||
@@ -214,9 +217,9 @@ class RemoteVideoMenu extends PureComponent<Props> {
|
||||
* @param {Object} state - Redux state.
|
||||
* @param {Object} ownProps - Properties of component.
|
||||
* @private
|
||||
* @returns {Props}
|
||||
* @returns {IProps}
|
||||
*/
|
||||
function _mapStateToProps(state, ownProps) {
|
||||
function _mapStateToProps(state: IReduxState, ownProps: any) {
|
||||
const kickOutEnabled = getFeatureFlag(state, KICK_OUT_ENABLED, true);
|
||||
const { participantId } = ownProps;
|
||||
const { remoteVideoMenu = {}, disableRemoteMute } = state['features/base/config'];
|
||||
@@ -1,42 +1,32 @@
|
||||
// @flow
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { createBreakoutRoomsEvent } from '../../../analytics/AnalyticsEvents';
|
||||
import { sendAnalytics } from '../../../analytics/functions';
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
import { IconRingGroup } from '../../../base/icons/svg';
|
||||
import { isLocalParticipantModerator } from '../../../base/participants/functions';
|
||||
import AbstractButton, { IProps as AbstractButtonProps } from '../../../base/toolbox/components/AbstractButton';
|
||||
import { sendParticipantToRoom } from '../../../breakout-rooms/actions';
|
||||
import { IRoom } from '../../../breakout-rooms/types';
|
||||
|
||||
export type Props = AbstractButtonProps & {
|
||||
|
||||
/**
|
||||
* The redux {@code dispatch} function.
|
||||
*/
|
||||
dispatch: Function,
|
||||
export interface IProps extends AbstractButtonProps {
|
||||
|
||||
/**
|
||||
* ID of the participant to send to breakout room.
|
||||
*/
|
||||
participantID: string,
|
||||
participantID: string;
|
||||
|
||||
/**
|
||||
* Room to send participant to.
|
||||
*/
|
||||
room: Object,
|
||||
|
||||
/**
|
||||
* Translation function.
|
||||
*/
|
||||
t: Function
|
||||
};
|
||||
room: IRoom;
|
||||
}
|
||||
|
||||
/**
|
||||
* An abstract remote video menu button which sends the remote participant to a breakout room.
|
||||
*/
|
||||
class SendToBreakoutRoom extends AbstractButton<Props, *> {
|
||||
class SendToBreakoutRoom extends AbstractButton<IProps> {
|
||||
accessibilityLabel = 'breakoutRooms.actions.sendToBreakoutRoom';
|
||||
icon = IconRingGroup;
|
||||
|
||||
@@ -70,9 +60,9 @@ class SendToBreakoutRoom extends AbstractButton<Props, *> {
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @param {Object} ownProps - Properties of component.
|
||||
* @returns {Props}
|
||||
* @returns {IProps}
|
||||
*/
|
||||
function mapStateToProps(state) {
|
||||
function mapStateToProps(state: IReduxState) {
|
||||
return {
|
||||
visible: isLocalParticipantModerator(state)
|
||||
};
|
||||
@@ -1,10 +1,9 @@
|
||||
// @flow
|
||||
|
||||
import React, { PureComponent } from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
import { Text, TextStyle, View, ViewStyle } from 'react-native';
|
||||
import { Divider } from 'react-native-paper';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState, IStore } from '../../../app/types';
|
||||
import Avatar from '../../../base/avatar/components/Avatar';
|
||||
import { hideSheet } from '../../../base/dialog/actions';
|
||||
import BottomSheet from '../../../base/dialog/components/native/BottomSheet';
|
||||
@@ -17,50 +16,49 @@ import SharedVideoButton from '../../../shared-video/components/native/SharedVid
|
||||
|
||||
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 ID of the participant for which this menu opened for.
|
||||
*/
|
||||
participantId: string,
|
||||
interface IProps {
|
||||
|
||||
/**
|
||||
* True if the menu is currently open, false otherwise.
|
||||
*/
|
||||
_isOpen: boolean,
|
||||
_isOpen: boolean;
|
||||
|
||||
/**
|
||||
* Whether the participant is present in the room or not.
|
||||
*/
|
||||
_isParticipantAvailable?: boolean,
|
||||
_isParticipantAvailable?: boolean;
|
||||
|
||||
/**
|
||||
* Display name of the participant retrieved from Redux.
|
||||
*/
|
||||
_participantDisplayName: string,
|
||||
_participantDisplayName: string;
|
||||
|
||||
/**
|
||||
* The Redux dispatch function.
|
||||
*/
|
||||
dispatch: IStore['dispatch'];
|
||||
|
||||
/**
|
||||
* The ID of the participant for which this menu opened for.
|
||||
*/
|
||||
participantId: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class to implement a popup menu that opens upon long pressing a fake participant thumbnail.
|
||||
*/
|
||||
class SharedVideoMenu extends PureComponent<Props> {
|
||||
class SharedVideoMenu extends PureComponent<IProps> {
|
||||
/**
|
||||
* Constructor of the component.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this._onCancel = this._onCancel.bind(this);
|
||||
@@ -89,7 +87,8 @@ class SharedVideoMenu extends PureComponent<Props> {
|
||||
<BottomSheet
|
||||
renderHeader = { this._renderMenuHeader }
|
||||
showSlidingView = { _isParticipantAvailable }>
|
||||
<Divider style = { styles.divider } />
|
||||
{/* @ts-ignore */}
|
||||
<Divider style = { styles.divider as ViewStyle } />
|
||||
<SharedVideoButton { ...buttonProps } />
|
||||
</BottomSheet>
|
||||
);
|
||||
@@ -117,11 +116,11 @@ class SharedVideoMenu extends PureComponent<Props> {
|
||||
<View
|
||||
style = { [
|
||||
bottomSheetStyles.sheet,
|
||||
styles.participantNameContainer ] }>
|
||||
styles.participantNameContainer ] as ViewStyle[] }>
|
||||
<Avatar
|
||||
participantId = { participantId }
|
||||
size = { AVATAR_SIZE } />
|
||||
<Text style = { styles.participantNameLabel }>
|
||||
<Text style = { styles.participantNameLabel as TextStyle }>
|
||||
{ this.props._participantDisplayName }
|
||||
</Text>
|
||||
</View>
|
||||
@@ -135,9 +134,9 @@ class SharedVideoMenu extends PureComponent<Props> {
|
||||
* @param {Object} state - Redux state.
|
||||
* @param {Object} ownProps - Properties of component.
|
||||
* @private
|
||||
* @returns {Props}
|
||||
* @returns {IProps}
|
||||
*/
|
||||
function _mapStateToProps(state, ownProps) {
|
||||
function _mapStateToProps(state: IReduxState, ownProps: any) {
|
||||
const { participantId } = ownProps;
|
||||
const isParticipantAvailable = getParticipantById(state, participantId);
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
// @flow
|
||||
|
||||
import Slider from '@react-native-community/slider';
|
||||
import _ from 'lodash';
|
||||
import React, { PureComponent } from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { View, ViewStyle } from 'react-native';
|
||||
import { withTheme } from 'react-native-paper';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import Icon from '../../../base/icons/components/Icon';
|
||||
import { IconVolumeUp } from '../../../base/icons/svg';
|
||||
import { setVolume } from '../../../participants-pane/actions.native';
|
||||
@@ -14,57 +13,55 @@ import { VOLUME_SLIDER_SCALE } from '../../constants';
|
||||
|
||||
import styles from './styles';
|
||||
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link VolumeSlider}.
|
||||
*/
|
||||
type Props = {
|
||||
interface IProps {
|
||||
|
||||
/**
|
||||
* Whether the participant enters the conference silent.
|
||||
*/
|
||||
_startSilent: boolean,
|
||||
_startSilent: boolean;
|
||||
|
||||
/**
|
||||
* The volume level for the participant.
|
||||
*/
|
||||
_volume: number,
|
||||
_volume: number;
|
||||
|
||||
/**
|
||||
* The redux dispatch function.
|
||||
*/
|
||||
dispatch: Function,
|
||||
dispatch: Function;
|
||||
|
||||
/**
|
||||
* The ID of the participant.
|
||||
*/
|
||||
participantID: string,
|
||||
participantID: string;
|
||||
|
||||
/**
|
||||
* Theme used for styles.
|
||||
*/
|
||||
theme: Object
|
||||
};
|
||||
theme: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} state of {@link VolumeSlider}.
|
||||
*/
|
||||
type State = {
|
||||
interface IState {
|
||||
|
||||
/**
|
||||
* The volume of the participant's audio element. The value will
|
||||
* be represented by a slider.
|
||||
*/
|
||||
volumeLevel: number
|
||||
};
|
||||
volumeLevel: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Component that renders the volume slider.
|
||||
*
|
||||
* @returns {React$Element<any>}
|
||||
*/
|
||||
class VolumeSlider extends PureComponent<Props, State> {
|
||||
_onVolumeChange: Function;
|
||||
class VolumeSlider extends PureComponent<IProps, IState> {
|
||||
_originalVolumeChange: Function;
|
||||
|
||||
/**
|
||||
@@ -73,7 +70,7 @@ class VolumeSlider extends PureComponent<Props, State> {
|
||||
* @param {Object} props - The read-only properties with which the new
|
||||
* instance is to be initialized.
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
@@ -100,7 +97,7 @@ class VolumeSlider extends PureComponent<Props, State> {
|
||||
const onVolumeChange = _startSilent ? undefined : this._onVolumeChange;
|
||||
|
||||
return (
|
||||
<View style = { styles.volumeSliderContainer } >
|
||||
<View style = { styles.volumeSliderContainer as ViewStyle } >
|
||||
<Icon
|
||||
size = { 24 }
|
||||
src = { IconVolumeUp } />
|
||||
@@ -110,7 +107,7 @@ class VolumeSlider extends PureComponent<Props, State> {
|
||||
minimumTrackTintColor = { palette.action01 }
|
||||
minimumValue = { 0 }
|
||||
onValueChange = { onVolumeChange }
|
||||
style = { styles.sliderContainer }
|
||||
style = { styles.sliderContainer as ViewStyle }
|
||||
thumbTintColor = { palette.ui10 }
|
||||
value = { volumeLevel } />
|
||||
</View>
|
||||
@@ -126,7 +123,7 @@ class VolumeSlider extends PureComponent<Props, State> {
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onVolumeChange(volumeLevel) {
|
||||
_onVolumeChange(volumeLevel: number) {
|
||||
const { dispatch, participantID } = this.props;
|
||||
|
||||
dispatch(setVolume(participantID, volumeLevel));
|
||||
@@ -139,9 +136,9 @@ class VolumeSlider extends PureComponent<Props, State> {
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @param {Object} ownProps - The own props of the component.
|
||||
* @returns {Props}
|
||||
* @returns {IProps}
|
||||
*/
|
||||
function mapStateToProps(state, ownProps): Object {
|
||||
function mapStateToProps(state: IReduxState, ownProps: any) {
|
||||
const { participantID } = ownProps;
|
||||
const { participantsVolume } = state['features/participants-pane'];
|
||||
const { startSilent } = state['features/base/config'];
|
||||
@@ -1,13 +1,11 @@
|
||||
// @flow
|
||||
import { WithTranslation } from 'react-i18next';
|
||||
|
||||
import { translate } from '../../base/i18n/functions';
|
||||
import ExpandedLabel, { Props as AbstractProps } from '../../base/label/components/native/ExpandedLabel';
|
||||
import ExpandedLabel, { IProps as AbstractProps } from '../../base/label/components/native/ExpandedLabel';
|
||||
|
||||
import { AUD_LABEL_COLOR } from './styles';
|
||||
|
||||
type Props = AbstractProps & {
|
||||
t: Function
|
||||
}
|
||||
type Props = AbstractProps & WithTranslation;
|
||||
|
||||
/**
|
||||
* A react {@code Component} that implements an expanded label as tooltip-like
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
@@ -13,13 +11,13 @@ import AbstractVideoQualityLabel, {
|
||||
} from './AbstractVideoQualityLabel';
|
||||
import styles from './styles';
|
||||
|
||||
type Props = AbstractProps & {
|
||||
interface IProps extends AbstractProps {
|
||||
|
||||
/**
|
||||
* Style of the component passed as props.
|
||||
*/
|
||||
style: ?StyleType
|
||||
};
|
||||
style?: StyleType;
|
||||
}
|
||||
|
||||
/**
|
||||
* React {@code Component} responsible for displaying a label that indicates
|
||||
@@ -30,7 +28,7 @@ type Props = AbstractProps & {
|
||||
* is kept consistent with web and in the future we may introduce the required
|
||||
* api and extend this component with actual quality indication.
|
||||
*/
|
||||
class VideoQualityLabel extends AbstractVideoQualityLabel<Props> {
|
||||
class VideoQualityLabel extends AbstractVideoQualityLabel<IProps> {
|
||||
|
||||
/**
|
||||
* Implements React {@link Component}'s render.
|
||||
@@ -46,7 +44,7 @@ class VideoQualityLabel extends AbstractVideoQualityLabel<Props> {
|
||||
}
|
||||
|
||||
return (
|
||||
<Label
|
||||
<Label // @ts-ignore
|
||||
style = { combineStyles(styles.indicatorAudioOnly, style) }
|
||||
text = { t('videoStatus.audioOnly') } />
|
||||
);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user