ref(TS) Convert some native components to TS (#13307)

pull/13333/head jitsi-meet_8648
Robert Pintilii 2 years ago committed by GitHub
parent a22db037c7
commit dc037bc8dd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 19
      package-lock.json
  2. 1
      package.json
  3. 1
      react/features/base/conference/reducer.ts
  4. 2
      react/features/base/config/configType.ts
  5. 45
      react/features/base/testing/components/TestConnectionInfo.tsx
  6. 3
      react/features/base/testing/components/TestHint.web.ts
  7. 8
      react/features/conference/components/AbstractInsecureRoomNameLabel.tsx
  8. 8
      react/features/conference/components/native/AlwaysOnLabels.tsx
  9. 110
      react/features/conference/components/native/Conference.tsx
  10. 15
      react/features/conference/components/native/ExpandedLabelPopup.tsx
  11. 8
      react/features/conference/components/native/InsecureRoomNameExpandedLabel.ts
  12. 5
      react/features/conference/components/native/InsecureRoomNameLabel.tsx
  13. 10
      react/features/conference/components/native/Labels.tsx
  14. 8
      react/features/conference/components/native/RaisedHandsCountExpandedLabel.ts
  15. 9
      react/features/conference/components/native/RaisedHandsCountLabel.tsx
  16. 44
      react/features/conference/components/native/TitleBar.tsx
  17. 1
      react/features/conference/components/native/carmode/CarMode.tsx
  18. 2
      react/features/conference/components/native/carmode/SoundDeviceButton.tsx
  19. 2
      react/features/conference/components/native/carmode/TitleBar.tsx
  20. 24
      react/features/conference/components/native/constants.ts
  21. 2
      react/features/filmstrip/components/native/Filmstrip.tsx
  22. 2
      react/features/filmstrip/functions.native.ts
  23. 11
      react/features/mobile/audio-mode/components/AudioDeviceToggleButton.ts
  24. 83
      react/features/mobile/audio-mode/components/AudioRoutePickerDialog.tsx
  25. 2
      react/features/mobile/audio-mode/components/styles.ts
  26. 2
      react/features/mobile/audio-mode/logger.ts
  27. 18
      react/features/mobile/audio-mode/middleware.ts
  28. 7
      react/features/mobile/audio-mode/reducer.ts
  29. 4
      react/features/mobile/background/actions.ts
  30. 9
      react/features/mobile/background/middleware.native.ts
  31. 0
      react/features/mobile/background/middleware.web.js
  32. 4
      react/features/mobile/background/reducer.ts
  33. 4
      react/features/mobile/call-integration/CallKit.ts
  34. 2
      react/features/mobile/call-integration/ConnectionService.ts
  35. 5
      react/features/mobile/call-integration/functions.ts
  36. 39
      react/features/mobile/call-integration/middleware.ts
  37. 2
      react/features/mobile/external-api/logger.ts
  38. 55
      react/features/mobile/external-api/middleware.ts
  39. 4
      react/features/mobile/full-screen/actions.ts
  40. 9
      react/features/mobile/full-screen/middleware.ts
  41. 5
      react/features/mobile/navigation/components/lobby/components/LobbyNavigationContainer.tsx
  42. 6
      react/features/mobile/navigation/components/welcome/functions.tsx
  43. 8
      react/features/mobile/navigation/middleware.ts
  44. 4
      react/features/mobile/permissions/middleware.ts
  45. 8
      react/features/mobile/picture-in-picture/actions.ts
  46. 20
      react/features/mobile/picture-in-picture/components/PictureInPictureButton.ts
  47. 2
      react/features/mobile/picture-in-picture/logger.ts
  48. 2
      react/features/mobile/proximity/middleware.ts
  49. 2
      react/features/mobile/wake-lock/middleware.ts
  50. 2
      react/features/mobile/watchos/actions.ts
  51. 2
      react/features/mobile/watchos/logger.ts
  52. 16
      react/features/mobile/watchos/middleware.ts
  53. 1
      react/features/recent-list/components/AbstractRecentList.tsx
  54. 1
      react/features/recent-list/components/styles.web.js
  55. 4
      react/features/recent-list/components/styles.web.ts
  56. 20
      react/features/toolbox/components/native/ScreenSharingAndroidButton.ts
  57. 7
      react/features/toolbox/components/native/ScreenSharingButton.tsx
  58. 29
      react/features/toolbox/components/native/ScreenSharingIosButton.tsx
  59. 4
      react/features/visitors/components/native/VisitorsCountLabel.tsx

19
package-lock.json generated

@ -135,6 +135,7 @@
"@types/react-dom": "17.0.14",
"@types/react-linkify": "1.0.1",
"@types/react-native": "0.68.9",
"@types/react-native-keep-awake": "2.0.3",
"@types/react-native-video": "5.0.14",
"@types/react-redux": "7.1.24",
"@types/react-window": "1.8.5",
@ -5826,6 +5827,15 @@
"@types/react": "^17"
}
},
"node_modules/@types/react-native-keep-awake": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@types/react-native-keep-awake/-/react-native-keep-awake-2.0.3.tgz",
"integrity": "sha512-0C4bUlGPHRpplbn+YnX2UO0/2joa0dhCPumas2fielGNJkS1FTJOrw9jFo4B0+LOtmsepXiy/ANTF/on0b2Ddw==",
"dev": true,
"dependencies": {
"@types/react": "*"
}
},
"node_modules/@types/react-native-video": {
"version": "5.0.14",
"resolved": "https://registry.npmjs.org/@types/react-native-video/-/react-native-video-5.0.14.tgz",
@ -23883,6 +23893,15 @@
"@types/react": "^17"
}
},
"@types/react-native-keep-awake": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@types/react-native-keep-awake/-/react-native-keep-awake-2.0.3.tgz",
"integrity": "sha512-0C4bUlGPHRpplbn+YnX2UO0/2joa0dhCPumas2fielGNJkS1FTJOrw9jFo4B0+LOtmsepXiy/ANTF/on0b2Ddw==",
"dev": true,
"requires": {
"@types/react": "*"
}
},
"@types/react-native-video": {
"version": "5.0.14",
"resolved": "https://registry.npmjs.org/@types/react-native-video/-/react-native-video-5.0.14.tgz",

@ -140,6 +140,7 @@
"@types/react-dom": "17.0.14",
"@types/react-linkify": "1.0.1",
"@types/react-native": "0.68.9",
"@types/react-native-keep-awake": "2.0.3",
"@types/react-native-video": "5.0.14",
"@types/react-redux": "7.1.24",
"@types/react-window": "1.8.5",

@ -46,6 +46,7 @@ export interface IJitsiConference {
authenticateAndUpgradeRole: Function;
avModerationApprove: Function;
avModerationReject: Function;
callUUID?: string;
createVideoSIPGWSession: Function;
dial: Function;
disableAVModeration: Function;

@ -170,6 +170,7 @@ export interface IConfig {
}>;
callDisplayName?: string;
callFlowsEnabled?: boolean;
callHandle?: string;
callStatsConfigParams?: {
additionalIDs?: {
customerID?: string;
@ -191,6 +192,7 @@ export interface IConfig {
};
callStatsID?: string;
callStatsSecret?: string;
callUUID?: string;
channelLastN?: number;
chromeExtensionBanner?: {
chromeExtensionsInfo?: Array<{ id: string; path: string; }>;

@ -1,6 +1,7 @@
import React, { Component, Fragment } from 'react';
import { connect } from 'react-redux';
import { IReduxState } from '../../../app/types';
import statsEmitter from '../../../connection-indicator/statsEmitter';
import { getLocalParticipant } from '../../participants/functions';
import { isTestModeEnabled } from '../functions';
@ -10,7 +11,7 @@ import TestHint from './TestHint';
/**
* Defines the TestConnectionInfo's properties.
*/
type Props = {
interface IProps {
/**
* The JitsiConference's connection state. It's the lib-jitsi-meet's event
@ -20,30 +21,30 @@ type Props = {
* 'conference.connectionInterrupted'
* 'conference.connectionRestored'.
*/
_conferenceConnectionState: string,
_conferenceConnectionState: string;
/**
* This will be a boolean converted to a string. The value will be 'true'
* once the conference is joined (the XMPP MUC room to be specific).
*/
_conferenceJoinedState: string,
_conferenceJoinedState: string;
/**
* The local participant's ID. Required to be able to observe the local RTP
* stats.
*/
_localUserId: string,
_localUserId: string;
/**
* The local participant's role.
*/
_localUserRole: string,
_localUserRole: string;
/**
* Indicates whether or not the test mode is currently on. Otherwise the
* TestConnectionInfo component will not render.
*/
_testMode: boolean
_testMode: boolean;
}
/**
@ -64,15 +65,15 @@ type State = {
/**
* The local download RTP bitrate.
*/
download: number,
download: number;
/**
* The local upload RTP bitrate.
*/
upload: number
}
}
}
upload: number;
};
};
};
/**
* The component will expose some of the app state to the jitsi-meet-torture
@ -81,8 +82,7 @@ type State = {
* this information, but there's no such option on React Native(maybe that's
* a good thing).
*/
class TestConnectionInfo extends Component<Props, State> {
_onStatsUpdated: Object => void;
class TestConnectionInfo extends Component<IProps, State> {
/**
* Initializes new <tt>TestConnectionInfo</tt> instance.
@ -90,7 +90,7 @@ class TestConnectionInfo extends Component<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._onStatsUpdated = this._onStatsUpdated.bind(this);
@ -114,7 +114,8 @@ class TestConnectionInfo extends Component<Props, State> {
* @returns {void}
* @private
*/
_onStatsUpdated(stats = {}) {
_onStatsUpdated(stats = { bitrate: { download: undefined,
upload: undefined } }) {
this.setState({
stats: {
bitrate: {
@ -143,7 +144,7 @@ class TestConnectionInfo extends Component<Props, State> {
* @inheritdoc
* returns {void}
*/
componentDidUpdate(prevProps: Props) {
componentDidUpdate(prevProps: IProps) {
if (prevProps._localUserId !== this.props._localUserId) {
statsEmitter.unsubscribeToClientStats(
prevProps._localUserId, this._onStatsUpdated);
@ -175,7 +176,7 @@ class TestConnectionInfo extends Component<Props, State> {
}
return (
<Fragment accessible = { false } >
<Fragment>
<TestHint
id = 'org.jitsi.meet.conference.connectionState'
value = { this.props._conferenceConnectionState } />
@ -184,7 +185,7 @@ class TestConnectionInfo extends Component<Props, State> {
value = { this.props._conferenceJoinedState } />
<TestHint
id = 'org.jitsi.meet.conference.grantModeratorAvailable'
value = { true } />
value = { 'true' } />
<TestHint
id = 'org.jitsi.meet.conference.localParticipantRole'
value = { this.props._localUserRole } />
@ -202,9 +203,9 @@ class TestConnectionInfo extends Component<Props, State> {
*
* @param {Object} state - The Redux state.
* @private
* @returns {Props}
* @returns {IProps}
*/
function _mapStateToProps(state) {
function _mapStateToProps(state: IReduxState) {
const conferenceJoined
= Boolean(state['features/base/conference'].conference);
const localParticipant = getLocalParticipant(state);
@ -212,8 +213,8 @@ function _mapStateToProps(state) {
return {
_conferenceConnectionState: state['features/testing'].connectionState,
_conferenceJoinedState: conferenceJoined.toString(),
_localUserId: localParticipant?.id,
_localUserRole: localParticipant?.role,
_localUserId: localParticipant?.id ?? '',
_localUserRole: localParticipant?.role ?? '',
_testMode: isTestModeEnabled(state)
};
}

@ -0,0 +1,3 @@
import { Component } from 'react';
export default Component;

@ -1,19 +1,15 @@
import React, { PureComponent } from 'react';
import { WithTranslation } from 'react-i18next';
import { IReduxState } from '../../app/types';
import isInsecureRoomName from '../../base/util/isInsecureRoomName';
interface IProps {
interface IProps extends WithTranslation {
/**
* True of the label should be visible.
*/
_visible: boolean;
/**
* Function to be used to translate i18n labels.
*/
t: Function;
}
/**

@ -1,5 +1,3 @@
// @flow
import React, { useCallback } from 'react';
import { TouchableOpacity } from 'react-native';
import { useDispatch } from 'react-redux';
@ -19,16 +17,16 @@ import {
LabelHitSlop
} from './constants';
type Props = {
interface IProps {
/**
* Creates a function to be invoked when the onPress of the touchables are
* triggered.
*/
createOnPress: Function
createOnPress: Function;
}
const AlwaysOnLabels = ({ createOnPress }: Props) => {
const AlwaysOnLabels = ({ createOnPress }: IProps) => {
const dispatch = useDispatch();
const openHighlightDialogCallback = useCallback(() => dispatch(openHighlightDialog()), [ dispatch ]);

@ -1,5 +1,3 @@
// @flow
import { useIsFocused } from '@react-navigation/native';
import React, { useEffect } from 'react';
import {
@ -8,12 +6,14 @@ import {
Platform,
SafeAreaView,
StatusBar,
View
View,
ViewStyle
} from 'react-native';
import { withSafeAreaInsets } from 'react-native-safe-area-context';
import { EdgeInsets, withSafeAreaInsets } from 'react-native-safe-area-context';
import { connect } from 'react-redux';
import { appNavigate } from '../../../app/actions';
import { IReduxState } from '../../../app/types';
import { FULLSCREEN_ENABLED, PIP_ENABLED } from '../../../base/flags/constants';
import { getFeatureFlag } from '../../../base/flags/functions';
import { getParticipantCount } from '../../../base/participants/functions';
@ -24,6 +24,7 @@ import {
ASPECT_RATIO_NARROW,
ASPECT_RATIO_WIDE
} from '../../../base/responsive-ui/constants';
import { StyleType } from '../../../base/styles/functions.any';
import TestConnectionInfo from '../../../base/testing/components/TestConnectionInfo';
import { isCalendarEnabled } from '../../../calendar-sync/functions.native';
import DisplayNameLabel from '../../../display-name/components/native/DisplayNameLabel';
@ -63,27 +64,27 @@ import styles from './styles';
/**
* The type of the React {@code Component} props of {@link Conference}.
*/
type Props = AbstractProps & {
interface IProps extends AbstractProps {
/**
* Application's aspect ratio.
*/
_aspectRatio: Symbol,
_aspectRatio: Symbol;
/**
* Whether the audio only is enabled or not.
*/
_audioOnlyEnabled: boolean,
_audioOnlyEnabled: boolean;
/**
* Branding styles for conference.
*/
_brandingStyles: Object,
_brandingStyles: StyleType;
/**
* Whether the calendar feature is enabled or not.
*/
_calendarEnabled: boolean,
_calendarEnabled: boolean;
/**
* The indicator which determines that we are still connecting to the
@ -91,101 +92,101 @@ type Props = AbstractProps & {
* joining the room. If truthy, then an activity/loading indicator will be
* rendered.
*/
_connecting: boolean,
_connecting: boolean;
/**
* Set to {@code true} when the filmstrip is currently visible.
*/
_filmstripVisible: boolean,
_filmstripVisible: boolean;
/**
* The indicator which determines whether fullscreen (immersive) mode is enabled.
*/
_fullscreenEnabled: boolean,
_fullscreenEnabled: boolean;
/**
* The indicator which determines if the conference type is one to one.
*/
_isOneToOneConference: boolean,
_isOneToOneConference: boolean;
/**
* The indicator which determines if the participants pane is open.
*/
_isParticipantsPaneOpen: boolean,
_isParticipantsPaneOpen: boolean;
/**
* The ID of the participant currently on stage (if any).
*/
_largeVideoParticipantId: string,
_largeVideoParticipantId: string;
/**
* Local participant's display name.
*/
_localParticipantDisplayName: string,
_localParticipantDisplayName: string;
/**
* Whether Picture-in-Picture is enabled.
*/
_pictureInPictureEnabled: boolean,
_pictureInPictureEnabled: boolean;
/**
* The indicator which determines whether the UI is reduced (to accommodate
* smaller display areas).
*/
_reducedUI: boolean,
/**
* The indicator which determines whether the Toolbox is visible.
*/
_toolboxVisible: boolean,
_reducedUI: boolean;
/**
* Indicates if we should auto-knock.
*/
_shouldEnableAutoKnock: boolean,
_shouldEnableAutoKnock: boolean;
/**
* Indicates whether the lobby screen should be visible.
*/
_showLobby: boolean,
_showLobby: boolean;
/**
* Indicates whether the car mode is enabled.
*/
_startCarMode: boolean,
_startCarMode: boolean;
/**
* The indicator which determines whether the Toolbox is visible.
*/
_toolboxVisible: boolean;
/**
* The redux {@code dispatch} function.
*/
dispatch: Function,
dispatch: Function;
/**
* Object containing the safe area insets.
*/
insets: Object,
insets: EdgeInsets;
/**
* Default prop for navigating between screen components(React Navigation).
*/
navigation: Object
};
navigation: any;
}
type State = {
/**
* The label that is currently expanded.
*/
visibleExpandedLabel: ?string
}
visibleExpandedLabel?: string;
};
/**
* The conference page of the mobile (i.e. React Native) application.
*/
class Conference extends AbstractConference<Props, State> {
class Conference extends AbstractConference<IProps, State> {
/**
* Timeout ref.
*/
_expandedLabelTimeout: Object;
_expandedLabelTimeout: any;
/**
* Initializes a new Conference instance.
@ -193,14 +194,14 @@ class Conference extends AbstractConference<Props, State> {
* @param {Object} props - The read-only properties with which the new
* instance is to be initialized.
*/
constructor(props) {
constructor(props: IProps) {
super(props);
this.state = {
visibleExpandedLabel: undefined
};
this._expandedLabelTimeout = React.createRef();
this._expandedLabelTimeout = React.createRef<number>();
// Bind event handlers so they are only bound once per instance.
this._onClick = this._onClick.bind(this);
@ -235,7 +236,7 @@ class Conference extends AbstractConference<Props, State> {
*
* @inheritdoc
*/
componentDidUpdate(prevProps) {
componentDidUpdate(prevProps: IProps) {
const {
_shouldEnableAutoKnock,
_showLobby,
@ -267,7 +268,7 @@ class Conference extends AbstractConference<Props, State> {
// Tear handling any hardware button presses for back navigation down.
BackHandler.removeEventListener('hardwareBackPress', this._onHardwareBackPress);
clearTimeout(this._expandedLabelTimeout.current);
clearTimeout(this._expandedLabelTimeout.current ?? 0);
}
/**
@ -301,8 +302,6 @@ class Conference extends AbstractConference<Props, State> {
);
}
_onClick: () => void;
/**
* Changes the value of the toolboxVisible state, thus allowing us to switch
* between Toolbox and Filmstrip and change their visibility.
@ -314,8 +313,6 @@ class Conference extends AbstractConference<Props, State> {
this._setToolboxVisible(!this.props._toolboxVisible);
}
_onHardwareBackPress: () => boolean;
/**
* Handles a hardware button press for back navigation. Enters Picture-in-Picture mode
* (if supported) or leaves the associated {@code Conference} otherwise.
@ -340,8 +337,6 @@ class Conference extends AbstractConference<Props, State> {
return true;
}
_createOnPress: (string) => void;
/**
* Creates a function to be invoked when the onPress of the touchables are
* triggered.
@ -350,7 +345,7 @@ class Conference extends AbstractConference<Props, State> {
* triggered.
* @returns {Function}
*/
_createOnPress(label) {
_createOnPress(label: string) {
return () => {
const { visibleExpandedLabel } = this.state;
@ -434,7 +429,7 @@ class Conference extends AbstractConference<Props, State> {
<View
pointerEvents = 'box-none'
style = { styles.toolboxAndFilmstripContainer }>
style = { styles.toolboxAndFilmstripContainer as ViewStyle }>
<Captions onPress = { this._onClick } />
@ -463,17 +458,17 @@ class Conference extends AbstractConference<Props, State> {
<SafeAreaView
pointerEvents = 'box-none'
style = {
_toolboxVisible
(_toolboxVisible
? styles.titleBarSafeViewColor
: styles.titleBarSafeViewTransparent }>
: styles.titleBarSafeViewTransparent) as ViewStyle }>
<TitleBar _createOnPress = { this._createOnPress } />
</SafeAreaView>
<SafeAreaView
pointerEvents = 'box-none'
style = {
_toolboxVisible
(_toolboxVisible
? [ styles.titleBarSafeViewTransparent, { top: this.props.insets.top + 50 } ]
: styles.titleBarSafeViewTransparent
: styles.titleBarSafeViewTransparent) as ViewStyle
}>
<View
pointerEvents = 'box-none'
@ -482,7 +477,7 @@ class Conference extends AbstractConference<Props, State> {
</View>
<View
pointerEvents = 'box-none'
style = { alwaysOnTitleBarStyles }>
style = { alwaysOnTitleBarStyles as ViewStyle }>
{/* eslint-disable-next-line react/jsx-no-bind */}
<AlwaysOnLabels createOnPress = { this._createOnPress } />
</View>
@ -532,7 +527,7 @@ class Conference extends AbstractConference<Props, State> {
* @returns {React$Element}
*/
_renderNotificationsContainer() {
const notificationsStyle = {};
const notificationsStyle: ViewStyle = {};
// In the landscape mode (wide) there's problem with notifications being
// shadowed by the filmstrip rendered on the right. This makes the "x"
@ -559,8 +554,6 @@ class Conference extends AbstractConference<Props, State> {
);
}
_setToolboxVisible: (boolean) => void;
/**
* Dispatches an action changing the visibility of the {@link Toolbox}.
*
@ -569,7 +562,7 @@ class Conference extends AbstractConference<Props, State> {
* {@code Toolbox} or {@code false} to hide it.
* @returns {void}
*/
_setToolboxVisible(visible) {
_setToolboxVisible(visible: boolean) {
this.props.dispatch(setToolboxVisible(visible));
}
}
@ -578,10 +571,11 @@ class Conference extends AbstractConference<Props, State> {
* Maps (parts of) the redux state to the associated {@code Conference}'s props.
*
* @param {Object} state - The redux state.
* @param {any} _ownProps - Component's own props.
* @private
* @returns {Props}
* @returns {IProps}
*/
function _mapStateToProps(state) {
function _mapStateToProps(state: IReduxState, _ownProps: any) {
const { isOpen } = state['features/participants-pane'];
const { aspectRatio, reducedUI } = state['features/base/responsive-ui'];
const { backgroundColor } = state['features/dynamic-branding'];
@ -627,7 +621,7 @@ export default withSafeAreaInsets(connect(_mapStateToProps)(props => {
return () => setPictureInPictureEnabled(false);
}, [ isFocused ]);
return (
return ( // @ts-ignore
<Conference { ...props } />
);
}));

@ -1,26 +1,25 @@
// @flow
import React from 'react';
import BaseTheme from '../../../base/ui/components/BaseTheme';
import { EXPANDED_LABELS } from './constants';
type Props = {
interface IProps {
/**
* The selected label to show details.
*/
visibleExpandedLabel: ?string
visibleExpandedLabel?: string;
}
const ExpandedLabelPopup = ({ visibleExpandedLabel }: Props) => {
const ExpandedLabelPopup = ({ visibleExpandedLabel }: IProps) => {
if (visibleExpandedLabel) {
const expandedLabel = EXPANDED_LABELS[visibleExpandedLabel];
const expandedLabel = EXPANDED_LABELS[visibleExpandedLabel as keyof typeof EXPANDED_LABELS];
if (expandedLabel) {
const LabelComponent = expandedLabel.component || expandedLabel;
const { props, alwaysOn } = expandedLabel || {};
const LabelComponent = expandedLabel.component;
const { props, alwaysOn } = expandedLabel;
const style = {
top: alwaysOn ? BaseTheme.spacing[6] : BaseTheme.spacing[1]
};

@ -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 { INSECURE_ROOM_NAME_LABEL_COLOR } from './styles';
type Props = AbstractProps & {
t: Function
}
type Props = AbstractProps & WithTranslation;
/**
* A react {@code Component} that implements an expanded label as tooltip-like

@ -1,8 +1,7 @@
// @flow
import React from 'react';
import { connect } from 'react-redux';
import { translate } from '../../../base/i18n/functions';
import { IconWarning } from '../../../base/icons/svg';
import Label from '../../../base/label/components/native/Label';
import AbstractInsecureRoomNameLabel, { _mapStateToProps } from '../AbstractInsecureRoomNameLabel';
@ -27,4 +26,4 @@ class InsecureRoomNameLabel extends AbstractInsecureRoomNameLabel {
}
}
export default connect(_mapStateToProps)(InsecureRoomNameLabel);
export default translate(connect(_mapStateToProps)(InsecureRoomNameLabel));

@ -1,5 +1,5 @@
import React, { Component } from 'react';
import { TouchableOpacity, View } from 'react-native';
import { TouchableOpacity, View, ViewStyle } from 'react-native';
import TranscribingLabel from '../../../transcribing/components/TranscribingLabel.native';
import VideoQualityLabel from '../../../video-quality/components/VideoQualityLabel.native';
@ -8,19 +8,19 @@ import InsecureRoomNameLabel from './InsecureRoomNameLabel';
import { LABEL_ID_INSECURE_ROOM_NAME, LABEL_ID_QUALITY, LABEL_ID_TRANSCRIBING, LabelHitSlop } from './constants';
import styles from './styles';
type Props = {
interface IProps {
/**
* Creates a function to be invoked when the onPress of the touchables are
* triggered.
*/
createOnPress: Function
createOnPress: Function;
}
/**
* A container that renders the conference indicators, if any.
*/
class Labels extends Component<Props> {
class Labels extends Component<IProps> {
/**
* Implements React {@code Component}'s render.
@ -32,7 +32,7 @@ class Labels extends Component<Props> {
<View pointerEvents = 'box-none'>
<View
pointerEvents = 'box-none'
style = { styles.indicatorContainer }>
style = { styles.indicatorContainer as ViewStyle }>
<TouchableOpacity
hitSlop = { LabelHitSlop }
onPress = {

@ -1,11 +1,9 @@
// @flow
import { WithTranslation } from 'react-i18next';
import { translate } from '../../../base/i18n/functions';
import ExpandedLabel, { Props as AbstractProps } from '../../../base/label/components/native/ExpandedLabel';
import ExpandedLabel, { IProps as AbstractProps } from '../../../base/label/components/native/ExpandedLabel';
type Props = AbstractProps & {
t: Function
}
type Props = AbstractProps & WithTranslation;
/**
* A react {@code Component} that implements an expanded label as tooltip-like

@ -1,6 +1,7 @@
import React from 'react';
import { useSelector } from 'react-redux';
import { IReduxState } from '../../../app/types';
import { IconRaiseHand } from '../../../base/icons/svg';
import Label from '../../../base/label/components/native/Label';
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
@ -8,17 +9,17 @@ import BaseTheme from '../../../base/ui/components/BaseTheme.native';
import styles from './styles';
const RaisedHandsCountLabel = () => {
const raisedHandsCount = useSelector(state =>
const raisedHandsCount = useSelector((state: IReduxState) =>
(state['features/base/participants'].raisedHandsQueue || []).length);
return raisedHandsCount > 0 && (
return raisedHandsCount > 0 ? (
<Label
icon = { IconRaiseHand }
iconColor = { BaseTheme.palette.uiBackground }
style = { styles.raisedHandsCountLabel }
text = { raisedHandsCount }
text = { `${raisedHandsCount}` }
textStyle = { styles.raisedHandsCountLabelText } />
);
) : null;
};
export default RaisedHandsCountLabel;

@ -1,9 +1,8 @@
// @flow
import React from 'react';
import { Text, View } from 'react-native';
import { Text, View, ViewStyle } from 'react-native';
import { connect } from 'react-redux';
import { IReduxState } from '../../../app/types';
import { getConferenceName, getConferenceTimestamp } from '../../../base/conference/functions';
import { CONFERENCE_TIMER_ENABLED, MEETING_NAME_ENABLED } from '../../../base/flags/constants';
import { getFeatureFlag } from '../../../base/flags/functions';
@ -17,44 +16,43 @@ import ConferenceTimer from '../ConferenceTimer';
import Labels from './Labels';
import styles from './styles';
type Props = {
interface IProps {
/**
* Creates a function to be invoked when the onPress of the touchables are
* triggered.
* Whether displaying the current conference timer is enabled or not.
*/
_createOnPress: Function,
_conferenceTimerEnabled: boolean;
/**
* Whether displaying the current conference timer is enabled or not.
* Creates a function to be invoked when the onPress of the touchables are
* triggered.
*/
_conferenceTimerEnabled: boolean,
_createOnPress: Function;
/**
* Name of the meeting we're currently in.
*/
_meetingName: string,
_meetingName: string;
/**
* Whether displaying the current meeting name is enabled or not.
*/
_meetingNameEnabled: boolean,
_meetingNameEnabled: boolean;
/**
* True if the navigation bar should be visible.
*/
_visible: boolean
};
_visible: boolean;
}
/**
* Implements a navigation bar component that is rendered on top of the
* conference screen.
*
* @param {Props} props - The React props passed to this component.
* @param {IProps} props - The React props passed to this component.
* @returns {JSX.Element}
*/
const TitleBar = (props: Props) => {
const TitleBar = (props: IProps) => {
const { _visible } = props;
if (!_visible) {
@ -63,22 +61,22 @@ const TitleBar = (props: Props) => {
return (
<View
style = { styles.titleBarWrapper }>
<View style = { styles.pipButtonContainer }>
style = { styles.titleBarWrapper as ViewStyle }>
<View style = { styles.pipButtonContainer as ViewStyle }>
<PictureInPictureButton styles = { styles.pipButton } />
</View>
<View
pointerEvents = 'box-none'
style = { styles.roomNameWrapper }>
style = { styles.roomNameWrapper as ViewStyle }>
{
props._conferenceTimerEnabled
&& <View style = { styles.roomTimerView }>
&& <View style = { styles.roomTimerView as ViewStyle }>
<ConferenceTimer textStyle = { styles.roomTimer } />
</View>
}
{
props._meetingNameEnabled
&& <View style = { styles.roomNameView }>
&& <View style = { styles.roomNameView as ViewStyle }>
<Text
numberOfLines = { 1 }
style = { styles.roomName }>
@ -106,9 +104,9 @@ const TitleBar = (props: Props) => {
* Maps part of the Redux store to the props of this component.
*
* @param {Object} state - The Redux state.
* @returns {Props}
* @returns {IProps}
*/
function _mapStateToProps(state) {
function _mapStateToProps(state: IReduxState) {
const { hideConferenceTimer, hideConferenceSubject } = state['features/base/config'];
const startTimestamp = getConferenceTimestamp(state);

@ -1,4 +1,3 @@
/* eslint-disable lines-around-comment */
import React, { useEffect } from 'react';
import { View, ViewStyle } from 'react-native';
import Orientation from 'react-native-orientation-locker';

@ -4,8 +4,6 @@ import { useDispatch } from 'react-redux';
import { openSheet } from '../../../../base/dialog/actions';
import Button from '../../../../base/ui/components/native/Button';
import { BUTTON_TYPES } from '../../../../base/ui/constants.native';
// eslint-disable-next-line lines-around-comment
// @ts-ignore
import AudioRoutePickerDialog from '../../../../mobile/audio-mode/components/AudioRoutePickerDialog';
import AudioIcon from './AudioIcon';

@ -10,8 +10,6 @@ import { JitsiRecordingConstants } from '../../../../base/lib-jitsi-meet';
import { getLocalParticipant } from '../../../../base/participants/functions';
import ConnectionIndicator from '../../../../connection-indicator/components/native/ConnectionIndicator';
import RecordingLabel from '../../../../recording/components/native/RecordingLabel';
// eslint-disable-next-line lines-around-comment
// @ts-ignore
import VideoQualityLabel from '../../../../video-quality/components/VideoQualityLabel.native';
import styles from './styles';

@ -1,4 +1,4 @@
// @flow
import React from 'react';
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
import RecordingExpandedLabel from '../../../recording/components/native/RecordingExpandedLabel';
@ -28,12 +28,22 @@ export const LABEL_ID_INSECURE_ROOM_NAME = 'insecure-room-name';
export const LABEL_ID_RAISED_HANDS_COUNT = 'raised-hands-count';
export const LABEL_ID_VISITORS_COUNT = 'visitors-count';
interface IExpandedLabel {
alwaysOn?: boolean;
component: React.ComponentType<any>;
props?: any;
}
/**
* The {@code ExpandedLabel} components to be rendered for the individual
* {@code Label}s.
*/
export const EXPANDED_LABELS = {
[LABEL_ID_QUALITY]: VideoQualityExpandedLabel,
export const EXPANDED_LABELS: {
[key: string]: IExpandedLabel;
} = {
[LABEL_ID_QUALITY]: {
component: VideoQualityExpandedLabel
},
[LABEL_ID_RECORDING]: {
component: RecordingExpandedLabel,
props: {
@ -48,8 +58,12 @@ export const EXPANDED_LABELS = {
},
alwaysOn: true
},
[LABEL_ID_TRANSCRIBING]: TranscribingExpandedLabel,
[LABEL_ID_INSECURE_ROOM_NAME]: InsecureRoomNameExpandedLabel,
[LABEL_ID_TRANSCRIBING]: {
component: TranscribingExpandedLabel
},
[LABEL_ID_INSECURE_ROOM_NAME]: {
component: InsecureRoomNameExpandedLabel
},
[LABEL_ID_RAISED_HANDS_COUNT]: {
component: RaisedHandsCountExpandedLabel,
alwaysOn: true

@ -69,7 +69,7 @@ interface IProps {
/**
* Object containing the safe area insets.
*/
insets: Object;
insets?: Object;
}
/**

@ -219,7 +219,7 @@ export function getFilmstripDimensions({
aspectRatio: Symbol;
clientHeight: number;
clientWidth: number;
insets: {
insets?: {
bottom?: number;
left?: number;
right?: number;

@ -1,5 +1,4 @@
import { connect } from 'react-redux';
import type { Dispatch } from 'redux';
import { openSheet } from '../../../base/dialog/actions';
import { translate } from '../../../base/i18n/functions';
@ -8,18 +7,10 @@ import AbstractButton, { IProps as AbstractButtonProps } from '../../../base/too
import AudioRoutePickerDialog from './AudioRoutePickerDialog';
type Props = AbstractButtonProps & {
/**
* The Redux dispatch function.
*/
dispatch: Dispatch<any>
};
/**
* Implements an {@link AbstractButton} to open the audio device list.
*/
class AudioDeviceToggleButton extends AbstractButton<Props, *> {
class AudioDeviceToggleButton extends AbstractButton<AbstractButtonProps> {
accessibilityLabel = 'toolbar.accessibilityLabel.audioRoute';
icon = IconVolumeUp;
label = 'toolbar.accessibilityLabel.audioRoute';

@ -1,8 +1,9 @@
import _ from 'lodash';
import React, { Component } from 'react';
import { NativeModules, Text, TouchableHighlight, View } from 'react-native';
import { NativeModules, Text, TextStyle, TouchableHighlight, View, ViewStyle } from 'react-native';
import { connect } from 'react-redux';
import { IReduxState } 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';
@ -15,6 +16,7 @@ import {
IconPhoneRinging,
IconVolumeUp
} from '../../../base/icons/svg';
import { StyleType } from '../../../base/styles/functions.any';
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
import styles from './styles';
@ -24,92 +26,92 @@ const { AudioMode } = NativeModules;
/**
* Type definition for a single entry in the device list.
*/
type Device = {
interface IDevice {
/**
* Name of the icon which will be rendered on the right.
*/
icon: Object,
icon: Function;
/**
* True if the element is selected (will be highlighted in blue),
* false otherwise.
*/
selected: boolean,
selected: boolean;
/**
* Text which will be rendered in the row.
*/
text: string,
text: string;
/**
* Device type.
*/
type: string,
type: string;
/**
* Unique device ID.
*/
uid: ?string
};
uid?: string;
}
/**
* "Raw" device, as returned by native.
*/
type RawDevice = {
export interface IRawDevice {
/**
* Display name for the device.
*/
name: ?string,
name?: string;
/**
* Is this device selected?
*/
selected: boolean,
selected: boolean;
/**
* Device type.
*/
type: string,
type: string;
/**
* Unique device ID.
*/
uid: ?string
};
uid?: string;
}
/**
* {@code AudioRoutePickerDialog}'s React {@code Component} prop types.
*/
type Props = {
interface IProps {
/**
* Object describing available devices.
*/
_devices: Array<RawDevice>,
_devices: Array<IRawDevice>;
/**
* Used for hiding the dialog when the selection was completed.
*/
dispatch: Function,
dispatch: Function;
/**
* Invoked to obtain translated strings.
*/
t: Function
};
t: Function;
}
/**
* {@code AudioRoutePickerDialog}'s React {@code Component} state types.
*/
type State = {
interface IState {
/**
* Array of available devices.
*/
devices: Array<Device>
};
devices: Array<IDevice>;
}
/**
* Maps each device type to a display name and icon.
@ -146,7 +148,7 @@ const deviceInfoMap = {
* Implements a React {@code Component} which prompts the user when a password
* is required to join a conference.
*/
class AudioRoutePickerDialog extends Component<Props, State> {
class AudioRoutePickerDialog extends Component<IProps, IState> {
state = {
/**
* Available audio devices, it will be set in
@ -160,7 +162,7 @@ class AudioRoutePickerDialog extends Component<Props, State> {
*
* @inheritdoc
*/
static getDerivedStateFromProps(props: Props) {
static getDerivedStateFromProps(props: IProps) {
const { _devices: devices, t } = props;
if (!devices) {
@ -170,7 +172,7 @@ class AudioRoutePickerDialog extends Component<Props, State> {
const audioDevices = [];
for (const device of devices) {
const infoMap = deviceInfoMap[device.type];
const infoMap = deviceInfoMap[device.type as keyof typeof deviceInfoMap];
// Skip devices with unknown type.
if (!infoMap) {
@ -206,25 +208,25 @@ class AudioRoutePickerDialog extends Component<Props, State> {
/**
* Initializes a new {@code PasswordRequiredPrompt} instance.
*
* @param {Props} props - The read-only React {@code Component} props with
* @param {IProps} props - The read-only React {@code Component} props with
* which the new instance is to be initialized.
*/
constructor(props: Props) {
constructor(props: IProps) {
super(props);
// Trigger an initial update.
AudioMode.updateDeviceList && AudioMode.updateDeviceList();
AudioMode.updateDeviceList?.();
}
/**
* Builds and returns a function which handles the selection of a device
* on the sheet. The selected device will be used by {@code AudioMode}.
*
* @param {Device} device - Object representing the selected device.
* @param {IDevice} device - Object representing the selected device.
* @private
* @returns {Function}
*/
_onSelectDeviceFn(device: Device) {
_onSelectDeviceFn(device: IDevice) {
return () => {
this.props.dispatch(hideSheet());
AudioMode.setAudioDevice(device.uid || device.type);
@ -234,11 +236,11 @@ class AudioRoutePickerDialog extends Component<Props, State> {
/**
* Renders a single device.
*
* @param {Device} device - Object representing a single device.
* @param {IDevice} device - Object representing a single device.
* @private
* @returns {ReactElement}
*/
_renderDevice(device: Device) {
_renderDevice(device: IDevice) {
const { icon, selected, text } = device;
const selectedStyle = selected ? styles.selectedText : {};
const borderRadiusHighlightStyles = {
@ -253,11 +255,14 @@ class AudioRoutePickerDialog extends Component<Props, State> {
onPress = { this._onSelectDeviceFn(device) }
style = { speakerDeviceIsNotSelected && borderRadiusHighlightStyles }
underlayColor = { BaseTheme.palette.ui04 } >
<View style = { styles.deviceRow } >
<View style = { styles.deviceRow as ViewStyle } >
<Icon
src = { icon }
style = { [ styles.deviceIcon, bottomSheetStyles.buttons.iconStyle, selectedStyle ] } />
<Text style = { [ styles.deviceText, bottomSheetStyles.buttons.labelStyle, selectedStyle ] } >
style = { [ styles.deviceIcon, bottomSheetStyles.buttons.iconStyle, selectedStyle
] as StyleType[] } />
<Text
style = { [ styles.deviceText, bottomSheetStyles.buttons.labelStyle, selectedStyle
] as TextStyle[] } >
{ text }
</Text>
</View>
@ -275,11 +280,11 @@ class AudioRoutePickerDialog extends Component<Props, State> {
const { t } = this.props;
return (
<View style = { styles.deviceRow } >
<View style = { styles.deviceRow as ViewStyle } >
<Icon
src = { deviceInfoMap.SPEAKER.icon }
style = { [ styles.deviceIcon, bottomSheetStyles.buttons.iconStyle ] } />
<Text style = { [ styles.deviceText, bottomSheetStyles.buttons.labelStyle ] } >
style = { [ styles.deviceIcon, bottomSheetStyles.buttons.iconStyle ] as StyleType[] } />
<Text style = { [ styles.deviceText, bottomSheetStyles.buttons.labelStyle ] as TextStyle[] } >
{ t('audioDevices.none') }
</Text>
</View>
@ -316,7 +321,7 @@ class AudioRoutePickerDialog extends Component<Props, State> {
* @param {Object} state - The Redux state.
* @returns {Object}
*/
function _mapStateToProps(state) {
function _mapStateToProps(state: IReduxState) {
return {
_devices: state['features/mobile/audio-mode'].devices
};

@ -1,5 +1,3 @@
// @flow
import { MD_ITEM_HEIGHT } from '../../../base/dialog/components/native/styles';
import { createStyleSheet } from '../../../base/styles/functions.any';
import BaseTheme from '../../../base/ui/components/BaseTheme.native';

@ -1,5 +1,3 @@
// @flow
import { getLogger } from '../../base/logging/functions';
export default getLogger('features/mobile/audio-mode');

@ -1,6 +1,7 @@
// @flow
import { NativeEventEmitter, NativeModules } from 'react-native';
import { AnyAction } from 'redux';
import { IStore } from '../../app/types';
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../../base/app/actionTypes';
import { SET_AUDIO_ONLY } from '../../base/audio-only/actionTypes';
import {
@ -75,7 +76,7 @@ MiddlewareRegistry.register(store => next => action => {
* @private
* @returns {void}
*/
function _appWillMount(store) {
function _appWillMount(store: IStore) {
const subscriptions = [
AudioModeEmitter.addListener(AudioMode.DEVICE_CHANGE_EVENT, _onDevicesUpdate, store)
];
@ -93,8 +94,9 @@ function _appWillMount(store) {
* @private
* @returns {void}
*/
function _onDevicesUpdate(devices) {
const { dispatch } = this; // eslint-disable-line no-invalid-this
function _onDevicesUpdate(devices: any) {
// @ts-ignore
const { dispatch } = this; // eslint-disable-line @typescript-eslint/no-invalid-this
dispatch({
type: _SET_AUDIOMODE_DEVICES,
@ -112,7 +114,7 @@ function _onDevicesUpdate(devices) {
* @private
* @returns {void}
*/
function _setSubscriptions({ getState }) {
function _setSubscriptions({ getState }: IStore) {
const { subscriptions } = getState()['features/mobile/audio-mode'];
if (subscriptions) {
@ -134,12 +136,12 @@ function _setSubscriptions({ getState }) {
* @private
* @returns {*} The value returned by {@code next(action)}.
*/
function _updateAudioMode({ getState }, next, action) {
function _updateAudioMode({ getState }: IStore, next: Function, action: AnyAction) {
const result = next(action);
const state = getState();
const conference = getCurrentConference(state);
const { enabled: audioOnly } = state['features/base/audio-only'];
let mode;
let mode: string;
if (getFeatureFlag(state, AUDIO_FOCUS_DISABLED, false)) {
return result;
@ -149,7 +151,7 @@ function _updateAudioMode({ getState }, next, action) {
mode = AudioMode.DEFAULT;
}
AudioMode.setMode(mode).catch(err => logger.error(`Failed to set audio mode ${String(mode)}: ${err}`));
AudioMode.setMode(mode).catch((err: any) => logger.error(`Failed to set audio mode ${String(mode)}: ${err}`));
return result;
}

@ -2,10 +2,13 @@ import ReducerRegistry from '../../base/redux/ReducerRegistry';
import { equals, set } from '../../base/redux/functions';
import { _SET_AUDIOMODE_DEVICES, _SET_AUDIOMODE_SUBSCRIPTIONS } from './actionTypes';
import { IRawDevice } from './components/AudioRoutePickerDialog';
export interface IMobileAudioModeState {
devices: Object[];
subscriptions: Object[];
devices: IRawDevice[];
subscriptions: {
remove: Function;
}[];
}
const DEFAULT_STATE = {

@ -1,5 +1,3 @@
// @flow
import { APP_STATE_CHANGED, _SET_APP_STATE_LISTENER } from './actionTypes';
/**
@ -12,7 +10,7 @@ import { APP_STATE_CHANGED, _SET_APP_STATE_LISTENER } from './actionTypes';
* listener: Function
* }}
*/
export function _setAppStateListener(listener: ?Function) {
export function _setAppStateListener(listener?: Function) {
return {
type: _SET_APP_STATE_LISTENER,
listener

@ -1,8 +1,7 @@
// @flow
import { AppState } from 'react-native';
import type { Dispatch } from 'redux';
import { AnyAction } from 'redux';
import { IStore } from '../../app/types';
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../../base/app/actionTypes';
import MiddlewareRegistry from '../../base/redux/MiddlewareRegistry';
@ -51,7 +50,7 @@ MiddlewareRegistry.register(store => next => action => {
* @private
* @returns {void}
*/
function _onAppStateChange(dispatch: Dispatch<any>, appState: string) {
function _onAppStateChange(dispatch: IStore['dispatch'], appState: string) {
dispatch(appStateChanged(appState));
}
@ -69,7 +68,7 @@ function _onAppStateChange(dispatch: Dispatch<any>, appState: string) {
* @private
* @returns {Object} The value returned by {@code next(action)}.
*/
function _setAppStateListenerF({ getState }, next, action) {
function _setAppStateListenerF({ getState }: IStore, next: Function, action: AnyAction) {
// Remove the old AppState listener and add the new one.
const { appStateListener: oldListener } = getState()['features/background'];
const result = next(action);

@ -1,3 +1,5 @@
import { AppStateStatus } from 'react-native';
import ReducerRegistry from '../../base/redux/ReducerRegistry';
import {
@ -7,7 +9,7 @@ import {
export interface IBackgroundState {
appState: string;
appStateListener?: Function;
appStateListener?: (state: AppStateStatus) => void;
}
/**

@ -1,6 +1,6 @@
import { NativeEventEmitter, NativeModules } from 'react-native';
import { getName } from '../../app/functions';
import { getName } from '../../app/functions.native';
/**
* Thin wrapper around Apple's CallKit functionality.
@ -35,7 +35,7 @@ if (CallKit) {
CallKit = {
...CallKit,
addListener: eventEmitter.addListener.bind(eventEmitter),
registerSubscriptions(context, delegate) {
registerSubscriptions(context: any, delegate: any) {
CallKit.setProviderConfiguration({
iconTemplateImageName: 'CallKitIcon',
localizedName: getName()

@ -11,7 +11,7 @@ if (ConnectionService) {
ConnectionService = {
...ConnectionService,
addListener: eventEmitter.addListener.bind(eventEmitter),
registerSubscriptions(context, delegate) {
registerSubscriptions(context: any, delegate: any) {
return [
ConnectionService.addListener(
'org.jitsi.meet:features/connection_service#disconnect',

@ -1,5 +1,4 @@
// @flow
import { IStateful } from '../../base/app/types';
import { CALL_INTEGRATION_ENABLED } from '../../base/flags/constants';
import { getFeatureFlag } from '../../base/flags/functions';
import { toState } from '../../base/redux/functions';
@ -11,7 +10,7 @@ import { toState } from '../../base/redux/functions';
* function.
* @returns {string} - Default URL for the app.
*/
export function isCallIntegrationEnabled(stateful: Function | Object) {
export function isCallIntegrationEnabled(stateful: IStateful) {
const state = toState(stateful);
const { disableCallIntegration } = state['features/base/settings'];
const flag = getFeatureFlag(state, CALL_INTEGRATION_ENABLED);

@ -1,11 +1,11 @@
// @flow
import { Alert, NativeModules, Platform } from 'react-native';
import { AnyAction } from 'redux';
import { v4 as uuidv4 } from 'uuid';
import { createTrackMutedEvent } from '../../analytics/AnalyticsEvents';
import { sendAnalytics } from '../../analytics/functions';
import { appNavigate } from '../../app/actions';
import { IReduxState, IStore } from '../../app/types';
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../../base/app/actionTypes';
import { SET_AUDIO_ONLY } from '../../base/audio-only/actionTypes';
import {
@ -19,6 +19,7 @@ import {
getConferenceName,
getCurrentConference
} from '../../base/conference/functions';
import { IJitsiConference } from '../../base/conference/reducer';
import { getInviteURL } from '../../base/connection/functions';
import { setAudioMuted } from '../../base/media/actions';
import { MEDIA_TYPE } from '../../base/media/constants';
@ -104,7 +105,7 @@ CallIntegration && MiddlewareRegistry.register(store => next => action => {
* @private
* @returns {*} The value returned by {@code next(action)}.
*/
function _appWillMount({ dispatch, getState }, next, action) {
function _appWillMount({ dispatch, getState }: IStore, next: Function, action: AnyAction) {
const result = next(action);
const context = {
@ -142,7 +143,7 @@ function _appWillMount({ dispatch, getState }, next, action) {
* @private
* @returns {*} The value returned by {@code next(action)}.
*/
function _conferenceFailed({ getState }, next, action) {
function _conferenceFailed({ getState }: IStore, next: Function, action: AnyAction) {
const result = next(action);
if (!isCallIntegrationEnabled(getState)) {
@ -177,7 +178,7 @@ function _conferenceFailed({ getState }, next, action) {
* @private
* @returns {*} The value returned by {@code next(action)}.
*/
function _conferenceJoined({ getState }, next, action) {
function _conferenceJoined({ getState }: IStore, next: Function, action: AnyAction) {
const result = next(action);
if (!isCallIntegrationEnabled(getState)) {
@ -222,7 +223,7 @@ function _conferenceJoined({ getState }, next, action) {
* @private
* @returns {*} The value returned by {@code next(action)}.
*/
function _conferenceLeft({ getState }, next, action) {
function _conferenceLeft({ getState }: IStore, next: Function, action: AnyAction) {
const result = next(action);
if (!isCallIntegrationEnabled(getState)) {
@ -252,7 +253,7 @@ function _conferenceLeft({ getState }, next, action) {
* @private
* @returns {*} The value returned by {@code next(action)}.
*/
function _conferenceWillJoin({ dispatch, getState }, next, action) {
function _conferenceWillJoin({ dispatch, getState }: IStore, next: Function, action: AnyAction) {
const result = next(action);
if (!isCallIntegrationEnabled(getState)) {
@ -292,7 +293,7 @@ function _conferenceWillJoin({ dispatch, getState }, next, action) {
_updateCallIntegrationMuted(conference, state);
}
})
.catch(error => {
.catch((error: any) => {
// Currently this error codes are emitted only by Android.
//
if (error.code === 'CREATE_OUTGOING_CALL_FAILED') {
@ -327,7 +328,7 @@ function _conferenceWillJoin({ dispatch, getState }, next, action) {
* @param {Object} state - Redux store.
* @returns {void}
*/
function _handleConnectionServiceFailure(state: Object) {
function _handleConnectionServiceFailure(state: IReduxState) {
const conference = getCurrentConference(state);
if (conference) {
@ -356,8 +357,9 @@ function _handleConnectionServiceFailure(state: Object) {
* {@code performEndCallAction}.
* @returns {void}
*/
function _onPerformEndCallAction({ callUUID }) {
const { dispatch, getState } = this; // eslint-disable-line no-invalid-this
function _onPerformEndCallAction({ callUUID }: { callUUID: string; }) {
// @ts-ignore
const { dispatch, getState } = this; // eslint-disable-line @typescript-eslint/no-invalid-this
const conference = getCurrentConference(getState);
if (conference && conference.callUUID === callUUID) {
@ -376,8 +378,9 @@ function _onPerformEndCallAction({ callUUID }) {
* {@code performSetMutedCallAction}.
* @returns {void}
*/
function _onPerformSetMutedCallAction({ callUUID, muted }) {
const { dispatch, getState } = this; // eslint-disable-line no-invalid-this
function _onPerformSetMutedCallAction({ callUUID, muted }: { callUUID: string; muted: boolean; }) {
// @ts-ignore
const { dispatch, getState } = this; // eslint-disable-line @typescript-eslint/no-invalid-this
const conference = getCurrentConference(getState);
if (conference && conference.callUUID === callUUID) {
@ -408,7 +411,7 @@ function _onPerformSetMutedCallAction({ callUUID, muted }) {
* @private
* @returns {*} The value returned by {@code next(action)}.
*/
function _setAudioOnly({ getState }, next, action) {
function _setAudioOnly({ getState }: IStore, next: Function, action: AnyAction) {
const result = next(action);
const state = getState();
@ -418,7 +421,7 @@ function _setAudioOnly({ getState }, next, action) {
const conference = getCurrentConference(state);
if (conference && conference.callUUID) {
if (conference?.callUUID) {
CallIntegration.updateCall(
conference.callUUID,
{ hasVideo: !action.audioOnly });
@ -442,7 +445,7 @@ function _setAudioOnly({ getState }, next, action) {
* @private
* @returns {*} The value returned by {@code next(action)}.
*/
function _setCallKitSubscriptions({ getState }, next, action) {
function _setCallKitSubscriptions({ getState }: IStore, next: Function, action: AnyAction) {
const { subscriptions } = getState()['features/call-integration'];
if (subscriptions) {
@ -466,7 +469,7 @@ function _setCallKitSubscriptions({ getState }, next, action) {
* @private
* @returns {*} The value returned by {@code next(action)}.
*/
function _syncTrackState({ getState }, next, action) {
function _syncTrackState({ getState }: IStore, next: Function, action: AnyAction) {
const result = next(action);
if (!isCallIntegrationEnabled(getState)) {
@ -504,7 +507,7 @@ function _syncTrackState({ getState }, next, action) {
* @private
* @returns {void}
*/
function _updateCallIntegrationMuted(conference, state) {
function _updateCallIntegrationMuted(conference: IJitsiConference, state: IReduxState) {
const muted = isLocalTrackMuted(state['features/base/tracks'], MEDIA_TYPE.AUDIO);
CallIntegration.setMuted(conference.callUUID, muted);

@ -1,5 +1,3 @@
// @flow
import { getLogger } from '../../base/logging/functions';
export default getLogger('features/mobile/external-api');

@ -1,10 +1,11 @@
// @flow
import debounce from 'lodash/debounce';
import { NativeEventEmitter, NativeModules } from 'react-native';
import { AnyAction } from 'redux';
// @ts-expect-error
import { ENDPOINT_TEXT_MESSAGE_NAME } from '../../../../modules/API/constants';
import { appNavigate } from '../../app/actions';
import { IStore } from '../../app/types';
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../../base/app/actionTypes';
import {
CONFERENCE_FAILED,
@ -19,6 +20,7 @@ import {
getCurrentConference,
isRoomValid
} from '../../base/conference/functions';
import { IJitsiConference } from '../../base/conference/reducer';
import { CONNECTION_DISCONNECTED } from '../../base/connection/actionTypes';
import {
JITSI_CONNECTION_CONFERENCE_KEY,
@ -36,10 +38,12 @@ import {
getRemoteParticipants,
isScreenShareParticipant
} from '../../base/participants/functions';
import { IParticipant } from '../../base/participants/types';
import MiddlewareRegistry from '../../base/redux/MiddlewareRegistry';
import StateListenerRegistry from '../../base/redux/StateListenerRegistry';
import { toggleScreensharing } from '../../base/tracks/actions.native';
import { getLocalTracks, isLocalTrackMuted } from '../../base/tracks/functions.native';
import { ITrack } from '../../base/tracks/types';
import { CLOSE_CHAT, OPEN_CHAT } from '../../chat/actionTypes';
import { openChat } from '../../chat/actions';
import { closeChat, sendMessage, setPrivateMessageRecipient } from '../../chat/actions.any';
@ -237,7 +241,7 @@ MiddlewareRegistry.register(store => next => action => {
*/
StateListenerRegistry.register(
/* selector */ state => state['features/base/tracks'],
/* listener */ debounce((tracks, store) => {
/* listener */ debounce((tracks: ITrack[], store: IStore) => {
const oldScreenShares = store.getState()['features/mobile/external-api'].screenShares || [];
const newScreenShares = tracks
.filter(track => track.mediaType === 'video' && track.videoType === 'desktop')
@ -277,7 +281,7 @@ StateListenerRegistry.register(
* @param {Participant} participant - The participant object from the redux store.
* @returns {Object} - The participant info object.
*/
function _participantToParticipantInfo(participant) {
function _participantToParticipantInfo(participant: IParticipant) {
return {
isLocal: participant.local,
email: participant.email,
@ -296,7 +300,7 @@ function _participantToParticipantInfo(participant) {
* @private
* @returns {void}
*/
function _registerForNativeEvents(store) {
function _registerForNativeEvents(store: IStore) {
const { getState, dispatch } = store;
eventEmitter.addListener(ExternalAPI.HANG_UP, () => {
@ -315,7 +319,7 @@ function _registerForNativeEvents(store) {
const conference = getCurrentConference(getState());
try {
conference && conference.sendEndpointMessage(to, {
conference?.sendEndpointMessage(to, {
name: ENDPOINT_TEXT_MESSAGE_NAME,
text: message
});
@ -334,7 +338,7 @@ function _registerForNativeEvents(store) {
const remoteParticipants = getRemoteParticipants(store);
const localParticipant = getLocalParticipant(store);
participantsInfo.push(_participantToParticipantInfo(localParticipant));
localParticipant && participantsInfo.push(_participantToParticipantInfo(localParticipant));
remoteParticipants.forEach(participant => {
if (!participant.fakeParticipant) {
participantsInfo.push(_participantToParticipantInfo(participant));
@ -401,12 +405,12 @@ function _unregisterForNativeEvents() {
* @private
* @returns {void}
*/
function _registerForEndpointTextMessages(store) {
function _registerForEndpointTextMessages(store: IStore) {
const conference = getCurrentConference(store.getState());
conference && conference.on(
conference?.on(
JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
(...args) => {
(...args: any[]) => {
if (args && args.length >= 2) {
const [ sender, eventData ] = args;
@ -422,9 +426,9 @@ function _registerForEndpointTextMessages(store) {
}
});
conference.on(
conference?.on(
JitsiConferenceEvents.MESSAGE_RECEIVED,
(id, message, timestamp) => {
(id: string, message: string, timestamp: number) => {
sendEvent(
store,
CHAT_MESSAGE_RECEIVED,
@ -437,10 +441,10 @@ function _registerForEndpointTextMessages(store) {
}
);
conference.on(
conference?.on(
JitsiConferenceEvents.PRIVATE_MESSAGE_RECEIVED,
(id, message, timestamp) => {
sendEvent(
(id: string, message: string, timestamp: number) => {
sendEvent(
store,
CHAT_MESSAGE_RECEIVED,
/* data */ {
@ -449,7 +453,7 @@ function _registerForEndpointTextMessages(store) {
isPrivate: true,
timestamp
});
}
}
);
}
@ -462,7 +466,7 @@ function _registerForEndpointTextMessages(store) {
* {@code error}.
*/
function _toErrorString(
error: Error | { message: ?string, name: ?string } | string) {
error: Error | { message?: string; name?: string; } | string) {
// XXX In lib-jitsi-meet and jitsi-meet we utilize errors in the form of
// strings, Error instances, and plain objects which resemble Error.
return (
@ -487,7 +491,7 @@ function _toErrorString(
* @param {Action} action - The redux action.
* @returns {void}
*/
function _maybeTriggerEarlyConferenceWillJoin(store, action) {
function _maybeTriggerEarlyConferenceWillJoin(store: IStore, action: AnyAction) {
const { locationURL } = store.getState()['features/base/connection'];
const { room } = action;
@ -518,18 +522,19 @@ function _normalizeUrl(url: URL) {
* @returns {void}
*/
function _sendConferenceEvent(
store: Object,
store: IStore,
action: {
conference: Object,
type: string,
url: ?string
conference: IJitsiConference;
isAudioMuted?: boolean;
type: string;
url?: string;
}) {
const { conference, type, ...data } = action;
// For these (redux) actions, conference identifies a JitsiConference
// instance. The external API cannot transport such an object so we have to
// transport an "equivalent".
if (conference) {
if (conference) { // @ts-ignore
data.url = _normalizeUrl(conference[JITSI_CONFERENCE_URL_KEY]);
const localTracks = getLocalTracks(store.getState()['features/base/tracks']);
@ -569,7 +574,7 @@ function _sendConferenceEvent(
* @returns {boolean} If the specified event is to not be sent, {@code true};
* otherwise, {@code false}.
*/
function _swallowConferenceLeft({ getState }, action, { url }) {
function _swallowConferenceLeft({ getState }: IStore, action: AnyAction, { url }: { url: string; }) {
// XXX Internally, we work with JitsiConference instances. Externally
// though, we deal with URL strings. The relation between the two is many to
// one so it's technically and practically possible (by externally loading
@ -602,7 +607,7 @@ function _swallowConferenceLeft({ getState }, action, { url }) {
* @returns {boolean} If the specified event is to not be sent, {@code true};
* otherwise, {@code false}.
*/
function _swallowEvent(store, action, data) {
function _swallowEvent(store: IStore, action: AnyAction, data: any) {
switch (action.type) {
case CONFERENCE_LEFT:
return _swallowConferenceLeft(store, action, data);

@ -1,5 +1,3 @@
// @flow
import { _SET_IMMERSIVE_LISTENER } from './actionTypes';
/**
@ -13,7 +11,7 @@ import { _SET_IMMERSIVE_LISTENER } from './actionTypes';
* listener: ?Function
* }}
*/
export function _setImmersiveListener(listener: ?Function) {
export function _setImmersiveListener(listener?: Function) {
return {
type: _SET_IMMERSIVE_LISTENER,
listener

@ -1,7 +1,8 @@
// @flow
// @ts-expect-error
import { Immersive } from 'react-native-immersive';
import { AnyAction } from 'redux';
import { IStore } from '../../app/types';
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../../base/app/actionTypes';
import { getCurrentConference } from '../../base/conference/functions';
import { isAnyDialogOpen } from '../../base/dialog/functions';
@ -69,7 +70,7 @@ StateListenerRegistry.register(
* @private
* @returns {void}
*/
function _onImmersiveChange({ getState }) {
function _onImmersiveChange({ getState }: IStore) {
const state = getState();
const { appState } = state['features/background'];
@ -115,7 +116,7 @@ function _setFullScreen(fullScreen: boolean) {
* @private
* @returns {Object} The value returned by {@code next(action)}.
*/
function _setImmersiveListenerF({ getState }, next, action) {
function _setImmersiveListenerF({ getState }: IStore, next: Function, action: AnyAction) {
// XXX The React Native module Immersive is only implemented on Android and
// throws on other platforms.
if (Platform.OS === 'android') {

@ -3,6 +3,7 @@ import { createStackNavigator } from '@react-navigation/stack';
import React from 'react';
import { useSelector } from 'react-redux';
import { IReduxState } from '../../../../../app/types';
import LobbyChatScreen from '../../../../../lobby/components/native/LobbyChatScreen';
import LobbyScreen from '../../../../../lobby/components/native/LobbyScreen';
import { screen } from '../../../routes';
@ -18,12 +19,14 @@ const LobbyStack = createStackNavigator();
const LobbyNavigationContainer = () => {
const { isLobbyChatActive }
= useSelector(state => state['features/chat']);
= useSelector((state: IReduxState) => state['features/chat']);
return (
<NavigationContainer
independent = { true }
ref = { lobbyNavigationContainerRef }
// @ts-ignore
theme = { navigationContainerTheme }>
<LobbyStack.Navigator
screenOptions = {{

@ -1,12 +1,10 @@
// @flow
import React from 'react';
import { GestureResponderEvent } from 'react-native';
import { IconArrowBack } from '../../../../base/icons/svg';
import HeaderNavigationButton
from '../HeaderNavigationButton';
/**
* Render header arrow back button for navigation.
*
@ -14,7 +12,7 @@ import HeaderNavigationButton
* function.
* @returns {ReactElement}
*/
export function renderArrowBackButton(onPress: Function) {
export function renderArrowBackButton(onPress: (e?: GestureResponderEvent | MouseEvent) => void) {
return (
<HeaderNavigationButton
onPress = { onPress }

@ -1,9 +1,11 @@
import { appNavigate } from '../../app/actions';
import { AnyAction } from 'redux';
import { appNavigate } from '../../app/actions.native';
import { IStore } from '../../app/types';
import { CONFERENCE_FAILED } from '../../base/conference/actionTypes';
import { JitsiConferenceErrors } from '../../base/lib-jitsi-meet';
import MiddlewareRegistry from '../../base/redux/MiddlewareRegistry';
MiddlewareRegistry.register(store => next => action => {
switch (action.type) {
@ -23,7 +25,7 @@ MiddlewareRegistry.register(store => next => action => {
* @param {Object} action - The Redux action.
* @returns {Object}
*/
function _conferenceFailed({ dispatch }, next, action) {
function _conferenceFailed({ dispatch }: IStore, next: Function, action: AnyAction) {
const { error } = action;
// We need to cover the case where knocking participant

@ -1,5 +1,3 @@
/* @flow */
import { Alert } from 'react-native';
import { isRoomValid } from '../../base/conference/functions';
@ -47,7 +45,7 @@ MiddlewareRegistry.register(store => next => action => {
* @private
* @returns {void}
*/
function _alertPermissionErrorWithSettings(trackType) {
function _alertPermissionErrorWithSettings(trackType: string) {
/* eslint-disable indent */
const message = trackType === 'video'
? i18next.t('dialog.permissionCameraRequiredError')

@ -1,8 +1,6 @@
// @flow
import { NativeModules } from 'react-native';
import type { Dispatch } from 'redux';
import { IStore } from '../../app/types';
import { PIP_ENABLED } from '../../base/flags/constants';
import { getFeatureFlag } from '../../base/flags/functions';
import Platform from '../../base/react/Platform.native';
@ -21,7 +19,7 @@ import logger from './logger';
* @returns {Function}
*/
export function enterPictureInPicture() {
return (dispatch: Dispatch<any>, getState: Function) => {
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
// XXX At the time of this writing this action can only be dispatched by
// the button which is on the conference view, which means that it's
// fine to enter PiP mode.
@ -37,7 +35,7 @@ export function enterPictureInPicture() {
p.then(
() => dispatch({ type: ENTER_PICTURE_IN_PICTURE }),
e => logger.warn(`Error entering PiP mode: ${e}`));
(e: string) => logger.warn(`Error entering PiP mode: ${e}`));
}
};
}

@ -1,33 +1,27 @@
// @flow
import { NativeModules, Platform } from 'react-native';
import { connect } from 'react-redux';
import { IReduxState } from '../../../app/types';
import { PIP_ENABLED, PIP_WHILE_SCREEN_SHARING_ENABLED } from '../../../base/flags/constants';
import { getFeatureFlag } from '../../../base/flags/functions';
import { translate } from '../../../base/i18n/functions';
import { IconArrowDown } from '../../../base/icons/svg';
import AbstractButton, { IProps as AbstractButtonProps } from '../../../base/toolbox/components/AbstractButton';
import { isLocalVideoTrackDesktop } from '../../../base/tracks/functions';
import { isLocalVideoTrackDesktop } from '../../../base/tracks/functions.native';
import { enterPictureInPicture } from '../actions';
type Props = AbstractButtonProps & {
interface IProps extends AbstractButtonProps {
/**
* Whether Picture-in-Picture is enabled or not.
*/
_enabled: boolean,
/**
* The redux {@code dispatch} function.
*/
dispatch: Function
};
_enabled: boolean;
}
/**
* An implementation of a button for entering Picture-in-Picture mode.
*/
class PictureInPictureButton extends AbstractButton<Props, *> {
class PictureInPictureButton extends AbstractButton<IProps> {
accessibilityLabel = 'toolbar.accessibilityLabel.pip';
icon = IconArrowDown;
label = 'toolbar.pip';
@ -63,7 +57,7 @@ class PictureInPictureButton extends AbstractButton<Props, *> {
* _enabled: boolean
* }}
*/
function _mapStateToProps(state) {
function _mapStateToProps(state: IReduxState) {
const pipEnabled = Boolean(getFeatureFlag(state, PIP_ENABLED));
const pipWhileScreenSharingEnabled = getFeatureFlag(state, PIP_WHILE_SCREEN_SHARING_ENABLED, false);

@ -1,5 +1,3 @@
// @flow
import { getLogger } from '../../base/logging/functions';
export default getLogger('features/mobile/pip');

@ -29,6 +29,6 @@ StateListenerRegistry.register(
* @private
* @returns {void}
*/
function _setProximityEnabled(enabled) {
function _setProximityEnabled(enabled: boolean) {
NativeModules.Proximity.setEnabled(Boolean(enabled));
}

@ -26,7 +26,7 @@ StateListenerRegistry.register(
* @private
* @returns {void}
*/
function _setWakeLock(wakeLock) {
function _setWakeLock(wakeLock: boolean) {
if (wakeLock) {
KeepAwake.activate();
} else {

@ -1,5 +1,3 @@
// @flow
import { SET_CONFERENCE_TIMESTAMP, SET_SESSION_ID, SET_WATCH_REACHABLE } from './actionTypes';
/**

@ -1,5 +1,3 @@
// @flow
import { getLogger } from '../../base/logging/functions';
export default getLogger('features/mobile/watchos');

@ -1,10 +1,10 @@
// @flow
import { NativeModules, Platform } from 'react-native';
import { updateApplicationContext, watchEvents } from 'react-native-watch-connectivity';
import { appNavigate } from '../../app/actions';
import { IStore } from '../../app/types';
import { APP_WILL_MOUNT } from '../../base/app/actionTypes';
import { IStateful } from '../../base/app/types';
import { CONFERENCE_JOINED } from '../../base/conference/actionTypes';
import { getCurrentConferenceUrl } from '../../base/connection/functions';
import { setAudioMuted } from '../../base/media/actions';
@ -68,7 +68,7 @@ watchOSEnabled && MiddlewareRegistry.register(store => next => action => {
* @private
* @returns {void}
*/
function _appWillMount({ dispatch, getState }) {
function _appWillMount({ dispatch, getState }: IStore) {
watchEvents.addListener('reachability', reachable => {
dispatch(setWatchReachable(reachable));
_updateApplicationContext(getState);
@ -96,7 +96,7 @@ function _appWillMount({ dispatch, getState }) {
}
break;
case CMD_JOIN_CONFERENCE: {
const newConferenceURL = message.data;
const newConferenceURL: any = message.data;
const oldConferenceURL = getCurrentConferenceUrl(getState());
if (oldConferenceURL !== newConferenceURL) {
@ -122,7 +122,7 @@ function _appWillMount({ dispatch, getState }) {
* @returns {number}
* @private
*/
function _getSessionId(stateful) {
function _getSessionId(stateful: IStateful) {
const state = toState(stateful);
return state['features/mobile/watchos'].sessionID;
@ -135,7 +135,7 @@ function _getSessionId(stateful) {
* @returns {Array<Object>}
* @private
*/
function _getRecentUrls(stateful) {
function _getRecentUrls(stateful: IStateful) {
const state = toState(stateful);
const recentURLs = state['features/recent-list'];
@ -154,7 +154,7 @@ function _getRecentUrls(stateful) {
* @returns {boolean}
* @private
*/
function _isAudioMuted(stateful) {
function _isAudioMuted(stateful: IStateful) {
const state = toState(stateful);
const { audio } = state['features/base/media'];
@ -169,7 +169,7 @@ function _isAudioMuted(stateful) {
* @private
* @returns {void}
*/
function _updateApplicationContext(stateful) {
function _updateApplicationContext(stateful: IStateful) {
const state = toState(stateful);
const { conferenceTimestamp, sessionID, watchReachable } = state['features/mobile/watchos'];

@ -7,7 +7,6 @@ import { IStore } from '../../app/types';
import AbstractPage from '../../base/react/components/AbstractPage';
import { Container, Text } from '../../base/react/components/index';
// @ts-ignore
import styles from './styles';
/**

@ -0,0 +1,4 @@
export default {
emptyListContainer: {},
emptyListText: {}
};

@ -1,7 +1,6 @@
// @flow
import { connect } from 'react-redux';
import { IReduxState } from '../../../app/types';
import { ANDROID_SCREENSHARING_ENABLED } from '../../../base/flags/constants';
import { getFeatureFlag } from '../../../base/flags/functions';
import { translate } from '../../../base/i18n/functions';
@ -13,28 +12,23 @@ import { isLocalVideoTrackDesktop } from '../../../base/tracks/functions.native'
/**
* The type of the React {@code Component} props of {@link ScreenSharingAndroidButton}.
*/
type Props = AbstractButtonProps & {
interface IProps extends AbstractButtonProps {
/**
* True if the button needs to be disabled.
*/
_disabled: boolean,
_disabled: boolean;
/**
* Whether video is currently muted or not.
*/
_screensharing: boolean,
/**
* The redux {@code dispatch} function.
*/
dispatch: Function
};
_screensharing: boolean;
}
/**
* An implementation of a button for toggling screen sharing.
*/
class ScreenSharingAndroidButton extends AbstractButton<Props, *> {
class ScreenSharingAndroidButton extends AbstractButton<IProps> {
accessibilityLabel = 'toolbar.accessibilityLabel.shareYourScreen';
icon = IconScreenshare;
label = 'toolbar.startScreenSharing';
@ -85,7 +79,7 @@ class ScreenSharingAndroidButton extends AbstractButton<Props, *> {
* _screensharing: boolean
* }}
*/
function _mapStateToProps(state): Object {
function _mapStateToProps(state: IReduxState) {
const enabled = getFeatureFlag(state, ANDROID_SCREENSHARING_ENABLED, true);
return {

@ -5,11 +5,8 @@ import { connect } from 'react-redux';
import { IReduxState } from '../../../app/types';
import { isDesktopShareButtonDisabled } from '../../functions.native';
// @ts-ignore
import ScreenSharingAndroidButton from './ScreenSharingAndroidButton.js';
// eslint-disable-next-line lines-around-comment
// @ts-ignore
import ScreenSharingIosButton from './ScreenSharingIosButton.js';
import ScreenSharingAndroidButton from './ScreenSharingAndroidButton';
import ScreenSharingIosButton from './ScreenSharingIosButton';
const ScreenSharingButton = (props: any) => (
<>

@ -1,10 +1,9 @@
// @flow
import React from 'react';
import { NativeModules, Platform, findNodeHandle } from 'react-native';
import { ScreenCapturePickerView } from 'react-native-webrtc';
import { connect } from 'react-redux';
import { IReduxState } from '../../../app/types';
import { IOS_SCREENSHARING_ENABLED } from '../../../base/flags/constants';
import { getFeatureFlag } from '../../../base/flags/functions';
import { translate } from '../../../base/i18n/functions';
@ -15,23 +14,18 @@ import { isLocalVideoTrackDesktop } from '../../../base/tracks/functions.native'
/**
* The type of the React {@code Component} props of {@link ScreenSharingIosButton}.
*/
type Props = AbstractButtonProps & {
interface IProps extends AbstractButtonProps {
/**
* True if the button needs to be disabled.
*/
_disabled: boolean,
_disabled: boolean;
/**
* Whether video is currently muted or not.
*/
_screensharing: boolean,
/**
* The redux {@code dispatch} function.
*/
dispatch: Function
};
_screensharing: boolean;
}
const styles = {
screenCapturePickerView: {
@ -42,9 +36,8 @@ const styles = {
/**
* An implementation of a button for toggling screen sharing on iOS.
*/
class ScreenSharingIosButton extends AbstractButton<Props, *> {
_nativeComponent: ?Object;
_setNativeComponent: Function;
class ScreenSharingIosButton extends AbstractButton<IProps> {
_nativeComponent: React.Component<any, any> | null;
accessibilityLabel = 'toolbar.accessibilityLabel.shareYourScreen';
icon = IconScreenshare;
@ -57,7 +50,7 @@ class ScreenSharingIosButton extends AbstractButton<Props, *> {
* @param {Object} props - The React {@code Component} props to initialize
* the new {@code ScreenSharingIosButton} instance with.
*/
constructor(props) {
constructor(props: IProps) {
super(props);
this._nativeComponent = null;
@ -77,7 +70,7 @@ class ScreenSharingIosButton extends AbstractButton<Props, *> {
<>
{ super.render() }
<ScreenCapturePickerView
ref = { this._setNativeComponent }
ref = { this._setNativeComponent } // @ts-ignore
style = { styles.screenCapturePickerView } />
</>
);
@ -90,7 +83,7 @@ class ScreenSharingIosButton extends AbstractButton<Props, *> {
* @param {ReactComponent} component - React Component.
* @returns {void}
*/
_setNativeComponent(component) {
_setNativeComponent(component: React.Component<any, any> | null) {
this._nativeComponent = component;
}
@ -139,7 +132,7 @@ class ScreenSharingIosButton extends AbstractButton<Props, *> {
* _disabled: boolean,
* }}
*/
function _mapStateToProps(state): Object {
function _mapStateToProps(state: IReduxState) {
const enabled = getFeatureFlag(state, IOS_SCREENSHARING_ENABLED, false);
return {

@ -28,14 +28,14 @@ const VisitorsCountLabel = () => {
const visitorsCount = useSelector((state: IReduxState) =>
state['features/visitors'].count || 0);
return !visitorsMode && visitorsCount > 0 && (
return !visitorsMode && visitorsCount > 0 ? (
<Label
icon = { IconUsers }
iconColor = { BaseTheme.palette.uiBackground }
style = { styles.raisedHandsCountLabel }
text = { `${getVisitorsShortText(visitorsCount)}` }
textStyle = { styles.raisedHandsCountLabelText } />
);
) : null;
};
export default VisitorsCountLabel;

Loading…
Cancel
Save