Compare commits

...

14 Commits

Author SHA1 Message Date
Jaya Allamsetty
f81446909c chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1626.0.0+a41aa571...v1629.0.0+1714bf07
2023-05-02 10:01:10 -04:00
damencho
b4115593c0 fix: Fix xmldom version to be used. 2023-05-02 08:29:10 -05:00
Hristo Terezov
f8bd8b616e feat(reactions): New button for web. 2023-05-02 08:20:35 -05:00
Hristo Terezov
be55ccd6f4 fix(main-Toolbox): Display the correct buttons.
If some of the buttons from the main toolbar are disabled we were
displaying buttons from the overflow menu in their place.
2023-05-02 08:20:35 -05:00
Horatiu Muresan
e7db18bd80 fix(dial-in) Place PIN on a new line (#13309) 2023-05-02 16:02:51 +03:00
Robert Pintilii
dff41e0fcb fix(chat) Fix horizontal scroll (#13308) 2023-05-02 15:53:30 +03:00
damencho
43be4324af fix: Fix room locking without visitors. 2023-05-02 06:25:37 -05:00
Saúl Ibarra Corretgé
c33baf4c96 fix(ios) avoid rejecting builds in progress in TestFlight 2023-05-02 10:21:30 +02:00
Saúl Ibarra Corretgé
f95a356025 feat(android) bump minimum API level to 24
Some of our dependencies (most notably WebRTC) have dropped it and we
can no longer claim to support API level 23).
2023-05-02 10:19:19 +02:00
Robert Pintilii
1ba7765898 ref(TS) Convert some native components to TS (#13281)
Remove some @ts-ignores
2023-05-02 11:09:38 +03:00
Robert Pintilii
0346fca434 fix(checkbox) Fix misalign when label has multiple lines (#13304) 2023-05-02 10:23:04 +03:00
Robert Pintilii
d267b2499d fix(chat) Fix name overflows chat bubble (#13303)
Revert color change of scroll corner
2023-05-02 10:22:49 +03:00
Дамян Минков
e3e5f1fbfa feat(visitors): Handles locked rooms for visitors. (#13296)
* feat(visitors): Handles locked rooms for visitors.

* squash: Handle locked room password on promotion.

* squash: quotes.

* squash: Renames main_domain to local_domain.

* squash: Renames fmuc_main_domain to main_domain.

Adds required config to point to the main virtual host of the main prosody. There are cases when the first visitor tries to join and there are not main participants as they are in the queue waiting for the vnode connect message and we cannot get dynamically the main domain.

* squash: Fix check for main_domain config.
2023-05-01 17:16:16 -05:00
robertpin
4697192b43 fix(keyboard-a11y) Remove space from click trigger 2023-05-01 16:50:23 +02:00
104 changed files with 1245 additions and 977 deletions

View File

@@ -19,7 +19,7 @@ buildscript {
ext {
buildToolsVersion = "31.0.0"
compileSdkVersion = 32
minSdkVersion = 23
minSdkVersion = 24
targetSdkVersion = 32
supportLibVersion = "28.0.0"

View File

@@ -74,10 +74,6 @@
a:active {
color: black;
}
&::-webkit-scrollbar-corner {
background: #3a3a3a;
}
}

View File

@@ -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
)

View File

@@ -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
View File

@@ -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",

View File

@@ -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"
}
},

View File

@@ -18,6 +18,7 @@ type ToolbarButtons = 'camera' |
'participants-pane' |
'profile' |
'raisehand' |
'reactions' |
'recording' |
'security' |
'select-background' |

View File

@@ -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.

View File

@@ -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.

View File

@@ -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();
}
}

View File

@@ -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();
}

View File

@@ -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) {

View File

@@ -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',

View File

@@ -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 (

View File

@@ -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;

View File

@@ -25,10 +25,6 @@ class ChatPrivacyDialog extends AbstractChatPrivacyDialog {
onSubmit = { this._onSendPrivateMessage } />
);
}
_onSendGroupMessage: () => boolean;
_onSendPrivateMessage: () => boolean;
}
export default translate(connect(_mapStateToProps, _mapDispatchToProps)(ChatPrivacyDialog));

View File

@@ -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 {

View File

@@ -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

View File

@@ -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: {

View File

@@ -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
})}

View File

@@ -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.
* {{

View File

@@ -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.
*

View File

@@ -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 }

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -97,7 +97,7 @@ class DialInNumber extends Component<IProps> {
{ phoneNumber }
</span>
</span>
<span className = 'spacer'>&nbsp;</span>
<br />
<span className = 'conference-id'>
<span className = 'info-label'>
{ t('info.dialInConferenceID') }

View File

@@ -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(() => {

View File

@@ -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)

View File

@@ -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)
};
}

View File

@@ -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.

View File

@@ -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);
}

View File

@@ -0,0 +1,5 @@
export enum IReactionsMenuParent {
Button = 1,
OverflowMenu = 2,
OverflowDrawer = 3
}

View File

@@ -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
};
}

View File

@@ -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>

View File

@@ -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 */

View File

@@ -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;
}

View File

@@ -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 {

View File

@@ -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.

View File

@@ -1,5 +1,3 @@
// @flow
import BaseTheme from '../../../../base/ui/components/BaseTheme.native';
/**

View File

@@ -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
);
}

View File

@@ -4,7 +4,6 @@ import { Text, View } from 'react-native';
import { translate } from '../../../base/i18n/functions';
// @ts-ignore
import styles from './styles';

View File

@@ -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';

View File

@@ -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
};
}

View File

@@ -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) {

View File

@@ -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';

View File

@@ -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 {

View File

@@ -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>

View File

@@ -1,5 +1,3 @@
// @flow
import React from 'react';
import { View } from 'react-native';
@@ -22,5 +20,4 @@ const SpeakerStatsList = () => {
);
};
export default SpeakerStatsList;

View File

@@ -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.
*

View File

@@ -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()}.
*

View File

@@ -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}.
*/

View File

@@ -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);

View File

@@ -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 } />

View File

@@ -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';

View File

@@ -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 }>

View File

@@ -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'];

View File

@@ -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';

View File

@@ -8,7 +8,6 @@ import { BUTTON_TYPES } from '../../../base/ui/constants.native';
import HangupMenu from './HangupMenu';
/**
* Button for showing the hangup menu.
*

View File

@@ -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)
};

View File

@@ -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));

View File

@@ -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),

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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'];

View File

@@ -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 {

View File

@@ -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();

View File

@@ -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>
);
};

View File

@@ -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),

View File

@@ -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' ]

View File

@@ -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.
*

View File

@@ -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

View File

@@ -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.

View File

@@ -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)
};
}

View File

@@ -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';

View File

@@ -1,5 +1,3 @@
// @flow
import { connect } from 'react-redux';
import { translate } from '../../../base/i18n/functions';

View File

@@ -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));

View File

@@ -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)
};

View File

@@ -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));

View File

@@ -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 ?? '')
};
}

View File

@@ -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)

View File

@@ -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>
);

View File

@@ -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)
};

View File

@@ -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));

View File

@@ -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)

View File

@@ -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 {

View File

@@ -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'];

View File

@@ -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)
};

View File

@@ -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);

View File

@@ -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'];

View File

@@ -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

View File

@@ -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