diff --git a/react/features/base/app/components/BaseApp.tsx b/react/features/base/app/components/BaseApp.tsx index 35b2c0145c..e26107b921 100644 --- a/react/features/base/app/components/BaseApp.tsx +++ b/react/features/base/app/components/BaseApp.tsx @@ -250,7 +250,7 @@ export default class BaseApp

extends Component { * @returns {Promise} */ _navigate(route: { - component?: ComponentType; + component?: ComponentType; href?: string; props?: Object; }): Promise { diff --git a/react/features/base/avatar/components/Avatar.tsx b/react/features/base/avatar/components/Avatar.tsx index 3cff264a29..e684026cd5 100644 --- a/react/features/base/avatar/components/Avatar.tsx +++ b/react/features/base/avatar/components/Avatar.tsx @@ -71,7 +71,7 @@ export interface IProps { /** * The size of the avatar. */ - size: number; + size?: number; /** * One of the expected status strings (e.g. 'available') to render a badge on the avatar, if necessary. diff --git a/react/features/calendar-sync/components/UpdateCalendarEventDialog.web.js b/react/features/calendar-sync/components/UpdateCalendarEventDialog.web.ts similarity index 100% rename from react/features/calendar-sync/components/UpdateCalendarEventDialog.web.js rename to react/features/calendar-sync/components/UpdateCalendarEventDialog.web.ts diff --git a/react/features/chat/components/AbstractChat.ts b/react/features/chat/components/AbstractChat.ts index 98b9cfb56e..ec6b6e5103 100644 --- a/react/features/chat/components/AbstractChat.ts +++ b/react/features/chat/components/AbstractChat.ts @@ -5,6 +5,7 @@ import { IReduxState, IStore } from '../../app/types'; import { getLocalParticipant } from '../../base/participants/functions'; import { sendMessage, setIsPollsTabFocused } from '../actions'; import { SMALL_WIDTH_THRESHOLD } from '../constants'; +import { IMessage } from '../reducer'; /** * The type of the React {@code Component} props of {@code AbstractChat}. @@ -34,7 +35,7 @@ export interface IProps extends WithTranslation { /** * All the chat messages in the conference. */ - _messages: Array; + _messages: IMessage[]; /** * Number of unread chat messages. @@ -141,6 +142,7 @@ export default class AbstractChat

extends Component

{ * props. * * @param {Object} state - The redux store/state. + * @param {any} _ownProps - Components' own props. * @private * @returns {{ * _isOpen: boolean, @@ -148,7 +150,7 @@ export default class AbstractChat

extends Component

{ * _showNamePrompt: boolean * }} */ -export function _mapStateToProps(state: IReduxState) { +export function _mapStateToProps(state: IReduxState, _ownProps: any) { const { isOpen, isPollsTabFocused, messages, nbUnreadMessages } = state['features/chat']; const { nbUnreadPolls } = state['features/polls']; const _localParticipant = getLocalParticipant(state); diff --git a/react/features/chat/components/AbstractMessageRecipient.ts b/react/features/chat/components/AbstractMessageRecipient.ts index 51581fdfe9..633927f1ad 100644 --- a/react/features/chat/components/AbstractMessageRecipient.ts +++ b/react/features/chat/components/AbstractMessageRecipient.ts @@ -67,9 +67,10 @@ export function _mapDispatchToProps(dispatch: Function) { * Maps part of the Redux store to the props of this component. * * @param {Object} state - The Redux state. + * @param {any} _ownProps - Components' own props. * @returns {IProps} */ -export function _mapStateToProps(state: IReduxState) { +export function _mapStateToProps(state: IReduxState, _ownProps: any) { const { privateMessageRecipient, lobbyMessageRecipient, isLobbyChatActive } = state['features/chat']; return { diff --git a/react/features/chat/components/web/Chat.js b/react/features/chat/components/web/Chat.tsx similarity index 92% rename from react/features/chat/components/web/Chat.js rename to react/features/chat/components/web/Chat.tsx index afa960e7a1..ef69929b3a 100644 --- a/react/features/chat/components/web/Chat.js +++ b/react/features/chat/components/web/Chat.tsx @@ -8,7 +8,7 @@ import PollsPane from '../../../polls/components/web/PollsPane'; import { toggleChat } from '../../actions.web'; import { CHAT_TABS } from '../../constants'; import AbstractChat, { - type Props, + IProps, _mapStateToProps } from '../AbstractChat'; @@ -23,7 +23,7 @@ import MessageRecipient from './MessageRecipient'; * React Component for holding the chat feature in a side panel that slides in * and out of view. */ -class Chat extends AbstractChat { +class Chat extends AbstractChat { /** * Reference to the React Component for displaying chat messages. Used for @@ -37,7 +37,7 @@ class Chat extends AbstractChat { * @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._messageContainerRef = React.createRef(); @@ -66,7 +66,6 @@ class Chat extends AbstractChat { onKeyDown = { this._onEscClick } > { _showNamePrompt @@ -76,15 +75,13 @@ class Chat extends AbstractChat { ); } - _onChatTabKeyDown: (KeyboardEvent) => void; - /** * Key press handler for the chat tab. * * @param {KeyboardEvent} event - The event. * @returns {void} */ - _onChatTabKeyDown(event) { + _onChatTabKeyDown(event: React.KeyboardEvent) { if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); event.stopPropagation(); @@ -92,15 +89,13 @@ class Chat extends AbstractChat { } } - _onEscClick: (KeyboardEvent) => void; - /** * Click handler for the chat sidenav. * * @param {KeyboardEvent} event - Esc key click to close the popup. * @returns {void} */ - _onEscClick(event) { + _onEscClick(event: React.KeyboardEvent) { if (event.key === 'Escape' && this.props._isOpen) { event.preventDefault(); event.stopPropagation(); @@ -108,15 +103,13 @@ class Chat extends AbstractChat { } } - _onPollsTabKeyDown: (KeyboardEvent) => void; - /** * Key press handler for the polls tab. * * @param {KeyboardEvent} event - The event. * @returns {void} */ - _onPollsTabKeyDown(event) { + _onPollsTabKeyDown(event: React.KeyboardEvent) { if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); event.stopPropagation(); @@ -201,10 +194,6 @@ class Chat extends AbstractChat { ); } - _onSendMessage: (string) => void; - - _onToggleChat: () => void; - /** * Toggles the chat window. * @@ -213,9 +202,6 @@ class Chat extends AbstractChat { _onToggleChat() { this.props.dispatch(toggleChat()); } - _onTogglePollsTab: () => void; - _onToggleChatTab: () => void; - _onChangeTab: (string) => void; /** * Change selected tab. @@ -223,7 +209,7 @@ class Chat extends AbstractChat { * @param {string} id - Id of the clicked tab. * @returns {void} */ - _onChangeTab(id) { + _onChangeTab(id: string) { id === CHAT_TABS.CHAT ? this._onToggleChatTab() : this._onTogglePollsTab(); } } diff --git a/react/features/chat/components/web/ChatHeader.js b/react/features/chat/components/web/ChatHeader.tsx similarity index 71% rename from react/features/chat/components/web/ChatHeader.js rename to react/features/chat/components/web/ChatHeader.tsx index 332177a1d9..38328ddf40 100644 --- a/react/features/chat/components/web/ChatHeader.js +++ b/react/features/chat/components/web/ChatHeader.tsx @@ -1,49 +1,48 @@ -// @flow - import React, { useCallback } from 'react'; -import { connect } from 'react-redux'; +import { useTranslation } from 'react-i18next'; +import { useDispatch } from 'react-redux'; -import { translate } from '../../../base/i18n/functions'; import Icon from '../../../base/icons/components/Icon'; import { IconCloseLarge } from '../../../base/icons/svg'; import { toggleChat } from '../../actions.web'; -type Props = { - - /** - * Function to be called when pressing the close button. - */ - onCancel: Function, +interface IProps { /** * An optional class name. */ - className: string, + className: string; /** * Whether the polls feature is enabled or not. */ - isPollsEnabled: boolean, + isPollsEnabled: boolean; /** - * Invoked to obtain translated strings. + * Function to be called when pressing the close button. */ - t: Function -}; + onCancel: Function; +} /** * Custom header of the {@code ChatDialog}. * * @returns {React$Element} */ -function Header({ onCancel, className, isPollsEnabled, t }: Props) { +function ChatHeader({ className, isPollsEnabled }: IProps) { + const dispatch = useDispatch(); + const { t } = useTranslation(); + + const onCancel = useCallback(() => { + dispatch(toggleChat()); + }, []); const onKeyPressHandler = useCallback(e => { if (onCancel && (e.key === ' ' || e.key === 'Enter')) { e.preventDefault(); onCancel(); } - }, [ onCancel ]); + }, []); return (

export function abstractMapStateToProps(state: IReduxState) { return { _notificationsVisible: shouldDisplayNotifications(state), - _room: state['features/base/conference'].room, + _room: state['features/base/conference'].room ?? '', _shouldDisplayTileView: shouldDisplayTileView(state) }; } diff --git a/react/features/conference/components/functions.any.js b/react/features/conference/components/functions.any.ts similarity index 83% rename from react/features/conference/components/functions.any.js rename to react/features/conference/components/functions.any.ts index 4a221e6613..4ebf9a0d19 100644 --- a/react/features/conference/components/functions.any.js +++ b/react/features/conference/components/functions.any.ts @@ -1,4 +1,4 @@ -// @flow +import { IReduxState } from '../../app/types'; import { CONFERENCE_INFO } from './constants'; @@ -8,7 +8,7 @@ import { CONFERENCE_INFO } from './constants'; * @param {Object} state - The redux state. * @returns {Object} The conferenceInfo object. */ -export const getConferenceInfo = (state: Object) => { +export const getConferenceInfo = (state: IReduxState) => { const { conferenceInfo } = state['features/base/config']; if (conferenceInfo) { diff --git a/react/features/conference/components/functions.native.js b/react/features/conference/components/functions.native.ts similarity index 91% rename from react/features/conference/components/functions.native.js rename to react/features/conference/components/functions.native.ts index 83bb99c573..2507c125f6 100644 --- a/react/features/conference/components/functions.native.js +++ b/react/features/conference/components/functions.native.ts @@ -1,3 +1,5 @@ +import { IReduxState } from '../../app/types'; + export * from './functions.any'; /** @@ -6,7 +8,7 @@ export * from './functions.any'; * @param {Object} state - The redux state. * @returns {boolean} Whether conference is connecting. */ -export const isConnecting = (state: Object) => { +export const isConnecting = (state: IReduxState) => { const { connecting, connection } = state['features/base/connection']; const { conference, diff --git a/react/features/conference/components/functions.web.js b/react/features/conference/components/functions.web.ts similarity index 96% rename from react/features/conference/components/functions.web.js rename to react/features/conference/components/functions.web.ts index 90dc0f2365..c17625a20c 100644 --- a/react/features/conference/components/functions.web.js +++ b/react/features/conference/components/functions.web.ts @@ -1,5 +1,3 @@ -// @flow - export * from './functions.any'; /** diff --git a/react/features/conference/components/web/Conference.js b/react/features/conference/components/web/Conference.tsx similarity index 92% rename from react/features/conference/components/web/Conference.js rename to react/features/conference/components/web/Conference.tsx index 4ab19a00ce..7a1dfde48a 100644 --- a/react/features/conference/components/web/Conference.js +++ b/react/features/conference/components/web/Conference.tsx @@ -1,10 +1,11 @@ -// @flow - import _ from 'lodash'; import React from 'react'; +import { WithTranslation } from 'react-i18next'; import { connect as reactReduxConnect } from 'react-redux'; +// @ts-expect-error import VideoLayout from '../../../../../modules/UI/videolayout/VideoLayout'; +import { IReduxState, IStore } from '../../../app/types'; import { getConferenceNameForTitle } from '../../../base/conference/functions'; import { connect, disconnect } from '../../../base/connection/actions.web'; import { isMobileBrowser } from '../../../base/environment/utils'; @@ -28,7 +29,7 @@ import JitsiPortal from '../../../toolbox/components/web/JitsiPortal'; import Toolbox from '../../../toolbox/components/web/Toolbox'; import { LAYOUT_CLASSNAMES } from '../../../video-layout/constants'; import { getCurrentLayout } from '../../../video-layout/functions.any'; -import { maybeShowSuboptimalExperienceNotification } from '../../functions'; +import { maybeShowSuboptimalExperienceNotification } from '../../functions.web'; import { AbstractConference, abstractMapStateToProps @@ -38,10 +39,6 @@ import type { AbstractProps } from '../AbstractConference'; import ConferenceInfo from './ConferenceInfo'; import { default as Notice } from './Notice'; - -declare var APP: Object; -declare var interfaceConfig: Object; - /** * DOM events for when full screen mode has changed. Different browsers need * different vendor prefixes. @@ -58,66 +55,58 @@ const FULL_SCREEN_EVENTS = [ /** * The type of the React {@code Component} props of {@link Conference}. */ -type Props = AbstractProps & { +interface IProps extends AbstractProps, WithTranslation { /** * The alpha(opacity) of the background. */ - _backgroundAlpha: number, + _backgroundAlpha?: number; /** * Are any overlays visible? */ - _isAnyOverlayVisible: boolean, + _isAnyOverlayVisible: boolean; /** * The CSS class to apply to the root of {@link Conference} to modify the * application layout. */ - _layoutClassName: string, + _layoutClassName: string; /** * The config specified interval for triggering mouseMoved iframe api events. */ - _mouseMoveCallbackInterval: number, + _mouseMoveCallbackInterval?: number; /** *Whether or not the notifications should be displayed in the overflow drawer. */ - _overflowDrawer: boolean, + _overflowDrawer: boolean; /** * Name for this conference room. */ - _roomName: string, + _roomName: string; /** * If lobby page is visible or not. */ - _showLobby: boolean, + _showLobby: boolean; /** * If prejoin page is visible or not. */ - _showPrejoin: boolean, + _showPrejoin: boolean; - dispatch: Function, - t: Function + dispatch: IStore['dispatch']; } /** * The conference page of the Web application. */ -class Conference extends AbstractConference { - _onFullScreenChange: Function; - _onMouseEnter: Function; - _onMouseLeave: Function; - _onMouseMove: Function; - _onShowToolbar: Function; - _onVidespaceTouchStart: Function; +class Conference extends AbstractConference { _originalOnMouseMove: Function; _originalOnShowToolbar: Function; - _setBackground: Function; /** * Initializes a new Conference instance. @@ -125,7 +114,7 @@ class Conference extends AbstractConference { * @param {Object} props - The read-only properties with which the new * instance is to be initialized. */ - constructor(props) { + constructor(props: IProps) { super(props); const { _mouseMoveCallbackInterval } = props; @@ -173,7 +162,7 @@ class Conference extends AbstractConference { * @inheritdoc * returns {void} */ - componentDidUpdate(prevProps) { + componentDidUpdate(prevProps: IProps) { if (this.props._shouldDisplayTileView === prevProps._shouldDisplayTileView) { return; @@ -284,7 +273,7 @@ class Conference extends AbstractConference { * @private * @returns {void} */ - _setBackground(element) { + _setBackground(element: HTMLDivElement) { if (!element) { return; } @@ -331,7 +320,7 @@ class Conference extends AbstractConference { * @private * @returns {void} */ - _onMouseEnter(event) { + _onMouseEnter(event: React.MouseEvent) { APP.API.notifyMouseEnter(event); } @@ -342,7 +331,7 @@ class Conference extends AbstractConference { * @private * @returns {void} */ - _onMouseLeave(event) { + _onMouseLeave(event: React.MouseEvent) { APP.API.notifyMouseLeave(event); } @@ -353,7 +342,7 @@ class Conference extends AbstractConference { * @private * @returns {void} */ - _onMouseMove(event) { + _onMouseMove(event: React.MouseEvent) { APP.API.notifyMouseMove(event); } @@ -397,9 +386,9 @@ class Conference extends AbstractConference { * * @param {Object} state - The Redux state. * @private - * @returns {Props} + * @returns {IProps} */ -function _mapStateToProps(state) { +function _mapStateToProps(state: IReduxState) { const { backgroundAlpha, mouseMoveCallbackInterval } = state['features/base/config']; const { overflowDrawer } = state['features/toolbox']; @@ -407,7 +396,7 @@ function _mapStateToProps(state) { ...abstractMapStateToProps(state), _backgroundAlpha: backgroundAlpha, _isAnyOverlayVisible: Boolean(getOverlayToRender(state)), - _layoutClassName: LAYOUT_CLASSNAMES[getCurrentLayout(state)], + _layoutClassName: LAYOUT_CLASSNAMES[getCurrentLayout(state) ?? ''], _mouseMoveCallbackInterval: mouseMoveCallbackInterval, _overflowDrawer: overflowDrawer, _roomName: getConferenceNameForTitle(state), diff --git a/react/features/conference/components/web/ConferenceInfo.js b/react/features/conference/components/web/ConferenceInfo.tsx similarity index 89% rename from react/features/conference/components/web/ConferenceInfo.js rename to react/features/conference/components/web/ConferenceInfo.tsx index bf35071014..344f5fea84 100644 --- a/react/features/conference/components/web/ConferenceInfo.js +++ b/react/features/conference/components/web/ConferenceInfo.tsx @@ -1,21 +1,19 @@ -// @flow - /* eslint-disable react/no-multi-comp */ - import React, { Component } from 'react'; import { connect } from 'react-redux'; +import { IReduxState, IStore } from '../../../app/types'; import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet'; import E2EELabel from '../../../e2ee/components/E2EELabel'; import HighlightButton from '../../../recording/components/Recording/web/HighlightButton'; import RecordingLabel from '../../../recording/components/web/RecordingLabel'; -import { showToolbox } from '../../../toolbox/actions'; +import { showToolbox } from '../../../toolbox/actions.web'; import { isToolboxVisible } from '../../../toolbox/functions.web'; import TranscribingLabel from '../../../transcribing/components/TranscribingLabel.web'; import VideoQualityLabel from '../../../video-quality/components/VideoQualityLabel.web'; import VisitorsCountLabel from '../../../visitors/components/web/VisitorsCountLabel'; import ConferenceTimer from '../ConferenceTimer'; -import { getConferenceInfo } from '../functions'; +import { getConferenceInfo } from '../functions.web'; import ConferenceInfoContainer from './ConferenceInfoContainer'; import InsecureRoomNameLabel from './InsecureRoomNameLabel'; @@ -27,25 +25,31 @@ import ToggleTopPanelLabel from './ToggleTopPanelLabel'; /** * The type of the React {@code Component} props of {@link Subject}. */ -type Props = { +interface IProps { /** * The conference info labels to be shown in the conference header. */ - _conferenceInfo: Object, - - /** - * Invoked to active other features of the app. - */ - dispatch: Function; + _conferenceInfo: { + alwaysVisible?: string[]; + autoHide?: string[]; + }; /** * Indicates whether the component should be visible or not. */ - _visible: boolean -}; + _visible: boolean; -const COMPONENTS = [ + /** + * Invoked to active other features of the app. + */ + dispatch: IStore['dispatch']; +} + +const COMPONENTS: Array<{ + Component: React.ComponentType; + id: string; +}> = [ { Component: HighlightButton, id: 'highlight-moment' @@ -107,14 +111,14 @@ const COMPONENTS = [ * @param {Object} props - The props of the component. * @returns {React$None} */ -class ConferenceInfo extends Component { +class ConferenceInfo extends Component { /** * Initializes a new {@code ConferenceInfo} 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); this._renderAutoHide = this._renderAutoHide.bind(this); @@ -122,8 +126,6 @@ class ConferenceInfo extends Component { this._onTabIn = this._onTabIn.bind(this); } - _onTabIn: () => void; - /** * Callback invoked when the component is focused to show the conference * info if necessary. @@ -136,8 +138,6 @@ class ConferenceInfo extends Component { } } - _renderAutoHide: () => void; - /** * Renders auto-hidden info header labels. * @@ -165,8 +165,6 @@ class ConferenceInfo extends Component { ); } - _renderAlwaysVisible: () => void; - /** * Renders the always visible info header labels. * @@ -223,7 +221,7 @@ class ConferenceInfo extends Component { * _conferenceInfo: Object * }} */ -function _mapStateToProps(state) { +function _mapStateToProps(state: IReduxState) { return { _visible: isToolboxVisible(state), _conferenceInfo: getConferenceInfo(state) diff --git a/react/features/conference/components/web/ConferenceInfoContainer.js b/react/features/conference/components/web/ConferenceInfoContainer.tsx similarity index 78% rename from react/features/conference/components/web/ConferenceInfoContainer.js rename to react/features/conference/components/web/ConferenceInfoContainer.tsx index 2bfbbad4e1..4a5a1bc6e2 100644 --- a/react/features/conference/components/web/ConferenceInfoContainer.js +++ b/react/features/conference/components/web/ConferenceInfoContainer.tsx @@ -1,28 +1,26 @@ -/* @flow */ - import React from 'react'; import { isAlwaysOnTitleBarEmpty } from '../functions.web'; -type Props = { +interface IProps { /** * The children components. */ - children: React$Node, + children: React.ReactNode; /** * Id of the component. */ - id?: string, + id?: string; /** * Whether this conference info container should be visible or not. */ - visible: boolean + visible: boolean; } -export default ({ visible, children, id }: Props) => ( +export default ({ visible, children, id }: IProps) => (
diff --git a/react/features/conference/components/web/Notice.js b/react/features/conference/components/web/Notice.js deleted file mode 100644 index 2070dce744..0000000000 --- a/react/features/conference/components/web/Notice.js +++ /dev/null @@ -1,61 +0,0 @@ -/* @flow */ - -import React, { Component } from 'react'; -import { connect } from 'react-redux'; - -import { translate } from '../../../base/i18n/functions'; - -declare var config: Object; - -type Props = { - _message?: string, -}; - -/** - * Notice react component. - * - * @class Notice - */ -class Notice extends Component { - - /** - * Implements React's {@link Component#render()}. - * - * @inheritdoc - * @returns {ReactElement} - */ - render() { - if (!this.props._message) { - return null; - } - - return ( -
- - { this.props._message } - -
- ); - } -} - -/** - * Maps (parts of) the Redux state to the associated - * {@code Notice}'s props. - * - * @param {Object} state - The Redux state. - * @private - * @returns {{ - * _message: string, - * }} - */ -function _mapStateToProps(state) { - const { - noticeMessage - } = state['features/base/config']; - - return { - _message: noticeMessage - }; -} -export default translate(connect(_mapStateToProps)(Notice)); diff --git a/react/features/conference/components/web/Notice.tsx b/react/features/conference/components/web/Notice.tsx new file mode 100644 index 0000000000..953a91af6a --- /dev/null +++ b/react/features/conference/components/web/Notice.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import { connect } from 'react-redux'; + +import { IReduxState } from '../../../app/types'; + +interface IProps { + _message?: string; +} + +const Notice = ({ _message }: IProps) => { + if (!_message) { + return null; + } + + return ( +
+ + {_message} + +
+ ); +}; + +/** + * Maps (parts of) the Redux state to the associated + * {@code Notice}'s props. + * + * @param {Object} state - The Redux state. + * @private + * @returns {{ + * _message: string, + * }} + */ +function _mapStateToProps(state: IReduxState) { + const { + noticeMessage + } = state['features/base/config']; + + return { + _message: noticeMessage + }; +} +export default connect(_mapStateToProps)(Notice); diff --git a/react/features/conference/components/web/RaisedHandsCountLabel.tsx b/react/features/conference/components/web/RaisedHandsCountLabel.tsx index 889e85e277..4a9901ed10 100644 --- a/react/features/conference/components/web/RaisedHandsCountLabel.tsx +++ b/react/features/conference/components/web/RaisedHandsCountLabel.tsx @@ -28,7 +28,7 @@ const RaisedHandsCountLabel = () => { dispatch(openParticipantsPane()); }, []); - return raisedHandsCount > 0 && ( 0 ? ( ); + ) : null; }; export default RaisedHandsCountLabel; diff --git a/react/features/conference/components/web/ToggleTopPanelLabel.tsx b/react/features/conference/components/web/ToggleTopPanelLabel.tsx index e85dd5eddb..54fc1f06e9 100644 --- a/react/features/conference/components/web/ToggleTopPanelLabel.tsx +++ b/react/features/conference/components/web/ToggleTopPanelLabel.tsx @@ -16,13 +16,13 @@ const ToggleTopPanelLabel = () => { dispatch(setTopPanelVisible(true)); }, []); - return topPanelHidden && ( ); + ) : null; }; export default ToggleTopPanelLabel; diff --git a/react/features/deep-linking/components/NoMobileApp.web.js b/react/features/deep-linking/components/NoMobileApp.web.tsx similarity index 83% rename from react/features/deep-linking/components/NoMobileApp.web.js rename to react/features/deep-linking/components/NoMobileApp.web.tsx index acff8b4e86..24c1282cd7 100644 --- a/react/features/deep-linking/components/NoMobileApp.web.js +++ b/react/features/deep-linking/components/NoMobileApp.web.tsx @@ -1,10 +1,9 @@ -/* @flow */ - import React, { Component } from 'react'; import { connect } from 'react-redux'; import { createDeepLinkingPageEvent } from '../../analytics/AnalyticsEvents'; import { sendAnalytics } from '../../analytics/functions'; +import { IReduxState } from '../../app/types'; import { IDeeplinkingConfig } from '../../base/config/configType'; @@ -12,20 +11,20 @@ import { IDeeplinkingConfig } from '../../base/config/configType'; * The type of the React {@code Component} props of * {@link NoMobileApp}. */ -type Props = { +interface IProps { /** * The deeplinking config. */ - _deeplinkingCfg: IDeeplinkingConfig, -}; + _deeplinkingCfg: IDeeplinkingConfig; +} /** * React component representing no mobile app page. * * @class NoMobileApp */ -class NoMobileApp

extends Component

{ +class NoMobileApp extends Component { /** * Implements the Component's componentDidMount method. * @@ -44,7 +43,8 @@ class NoMobileApp

extends Component

{ */ render() { const ns = 'no-mobile-app'; - const { desktop: { appName } } = this.props._deeplinkingCfg; + const { desktop } = this.props._deeplinkingCfg; + const { appName } = desktop ?? {}; return (

@@ -66,9 +66,9 @@ class NoMobileApp

extends Component

{ * * @param {Object} state - The Redux state. * @private - * @returns {Props} + * @returns {IProps} */ -function _mapStateToProps(state) { +function _mapStateToProps(state: IReduxState) { return { _deeplinkingCfg: state['features/base/config'].deeplinking || {} }; diff --git a/react/features/filmstrip/components/web/Filmstrip.tsx b/react/features/filmstrip/components/web/Filmstrip.tsx index f3004364ce..a84c55b0e0 100644 --- a/react/features/filmstrip/components/web/Filmstrip.tsx +++ b/react/features/filmstrip/components/web/Filmstrip.tsx @@ -872,7 +872,7 @@ class Filmstrip extends PureComponent { * @private * @returns {IProps} */ -function _mapStateToProps(state: IReduxState, ownProps: Partial) { +function _mapStateToProps(state: IReduxState, ownProps: any) { const { _hasScroll = false, filmstripType, _topPanelFilmstrip, _remoteParticipants } = ownProps; const toolbarButtons = getToolbarButtons(state); const { iAmRecorder } = state['features/base/config']; diff --git a/react/features/filmstrip/components/web/MainFilmstrip.js b/react/features/filmstrip/components/web/MainFilmstrip.tsx similarity index 74% rename from react/features/filmstrip/components/web/MainFilmstrip.js rename to react/features/filmstrip/components/web/MainFilmstrip.tsx index 2ef35d7bb3..ce79ac8b90 100644 --- a/react/features/filmstrip/components/web/MainFilmstrip.js +++ b/react/features/filmstrip/components/web/MainFilmstrip.tsx @@ -1,7 +1,7 @@ -// @flow import React from 'react'; import { connect } from 'react-redux'; +import { IReduxState } from '../../../app/types'; import { getToolbarButtons } from '../../../base/config/functions.web'; import { isMobileBrowser } from '../../../base/environment/utils'; import { LAYOUTS } from '../../../video-layout/constants'; @@ -17,80 +17,80 @@ import { isFilmstripResizable, showGridInVerticalView } from '../../functions.we import Filmstrip from './Filmstrip'; -type Props = { +interface IProps { /** * The number of columns in tile view. */ - _columns: number, - - /** - * The width of the filmstrip. - */ - _filmstripWidth: number, + _columns: number; /** * The height of the filmstrip. */ - _filmstripHeight: number, + _filmstripHeight?: number; + + /** + * The width of the filmstrip. + */ + _filmstripWidth?: number; /** * Whether the filmstrip has scroll or not. */ - _hasScroll: boolean, + _hasScroll: boolean; /** * Whether or not the current layout is vertical filmstrip. */ - _isVerticalFilmstrip: boolean, + _isVerticalFilmstrip: boolean; /** * The participants in the call. */ - _remoteParticipants: Array, + _remoteParticipants: Array; /** * The length of the remote participants array. */ - _remoteParticipantsLength: number, + _remoteParticipantsLength: number; /** * Whether or not the filmstrip should be user-resizable. */ - _resizableFilmstrip: boolean, + _resizableFilmstrip: boolean; /** * The number of rows in tile view. */ - _rows: number, + _rows: number; /** * The height of the thumbnail. */ - _thumbnailHeight: number, + _thumbnailHeight?: number; /** * The width of the thumbnail. */ - _thumbnailWidth: number, + _thumbnailWidth?: number; /** * Whether or not the vertical filmstrip should have a background color. */ - _verticalViewBackground: boolean, + _verticalViewBackground: boolean; /** * Whether or not the vertical filmstrip should be displayed as grid. */ - _verticalViewGrid: boolean, + _verticalViewGrid: boolean; /** * Additional CSS class names to add to the container of all the thumbnails. */ - _videosClassName: string -}; + _videosClassName: string; +} -const MainFilmstrip = (props: Props) => ( +const MainFilmstrip = (props: IProps) => ( ( * Maps (parts of) the Redux state to the associated {@code Filmstrip}'s props. * * @param {Object} state - The Redux state. + * @param {any} _ownProps - Components' own props. * @private - * @returns {Props} + * @returns {IProps} */ -function _mapStateToProps(state) { +function _mapStateToProps(state: IReduxState, _ownProps: any) { const toolbarButtons = getToolbarButtons(state); const { remoteParticipants, width: verticalFilmstripWidth } = state['features/filmstrip']; const reduceHeight = state['features/toolbox'].visible && toolbarButtons.length; const { - gridDimensions: dimensions = {}, + gridDimensions: dimensions = { columns: undefined, + rows: undefined }, filmstripHeight, filmstripWidth, hasScroll: tileViewHasScroll, thumbnailSize: tileViewThumbnailSize - } = state['features/filmstrip'].tileViewDimensions; + } = state['features/filmstrip'].tileViewDimensions ?? {}; const _currentLayout = getCurrentLayout(state); const _resizableFilmstrip = isFilmstripResizable(state); const _verticalViewGrid = showGridInVerticalView(state); @@ -123,7 +125,7 @@ function _mapStateToProps(state) { let _hasScroll = false; const { clientHeight, clientWidth } = state['features/base/responsive-ui']; - const availableSpace = clientHeight - filmstripHeight; + const availableSpace = clientHeight - Number(filmstripHeight); let filmstripPadding = 0; if (availableSpace > 0) { @@ -150,7 +152,8 @@ function _mapStateToProps(state) { case LAYOUTS.TILE_VIEW: _hasScroll = Boolean(tileViewHasScroll); _thumbnailSize = tileViewThumbnailSize; - remoteFilmstripHeight = filmstripHeight - (collapseTileView && filmstripPadding > 0 ? filmstripPadding : 0); + remoteFilmstripHeight = Number(filmstripHeight) - ( + collapseTileView && filmstripPadding > 0 ? filmstripPadding : 0); remoteFilmstripWidth = filmstripWidth; break; case LAYOUTS.VERTICAL_FILMSTRIP_VIEW: @@ -163,14 +166,15 @@ function _mapStateToProps(state) { } = state['features/filmstrip'].verticalViewDimensions; _hasScroll = Boolean(hasScroll); - remoteFilmstripHeight = remoteVideosContainer?.height - (!_verticalViewGrid && shouldReduceHeight + remoteFilmstripHeight = Number(remoteVideosContainer?.height) - (!_verticalViewGrid && shouldReduceHeight ? TOOLBAR_HEIGHT : 0); remoteFilmstripWidth = remoteVideosContainer?.width; if (_verticalViewGrid) { - gridDimensions = gridView.gridDimensions; - _thumbnailSize = gridView.thumbnailSize; - _hasScroll = gridView.hasScroll; + gridDimensions = gridView?.gridDimensions ?? { columns: undefined, + rows: undefined }; + _thumbnailSize = gridView?.thumbnailSize; + _hasScroll = Boolean(gridView?.hasScroll); } else { _thumbnailSize = remote; } @@ -188,17 +192,18 @@ function _mapStateToProps(state) { } return { - _columns: gridDimensions.columns, + _columns: gridDimensions.columns ?? 1, _filmstripHeight: remoteFilmstripHeight, _filmstripWidth: remoteFilmstripWidth, _hasScroll, _remoteParticipants: remoteParticipants, _resizableFilmstrip, - _rows: gridDimensions.rows, + _rows: gridDimensions.rows ?? 1, _thumbnailWidth: _thumbnailSize?.width, _thumbnailHeight: _thumbnailSize?.height, _verticalViewGrid, - _verticalViewBackground: verticalFilmstripWidth.current + FILMSTRIP_BREAKPOINT_OFFSET >= FILMSTRIP_BREAKPOINT + _verticalViewBackground: Number(verticalFilmstripWidth.current) + + FILMSTRIP_BREAKPOINT_OFFSET >= FILMSTRIP_BREAKPOINT }; } diff --git a/react/features/filmstrip/components/web/ScreenshareFilmstrip.js b/react/features/filmstrip/components/web/ScreenshareFilmstrip.tsx similarity index 66% rename from react/features/filmstrip/components/web/ScreenshareFilmstrip.js rename to react/features/filmstrip/components/web/ScreenshareFilmstrip.tsx index 0dbc3c06ef..3415fddb98 100644 --- a/react/features/filmstrip/components/web/ScreenshareFilmstrip.js +++ b/react/features/filmstrip/components/web/ScreenshareFilmstrip.tsx @@ -1,106 +1,110 @@ -// @flow import React from 'react'; import { connect } from 'react-redux'; +import { IReduxState } from '../../../app/types'; import { LAYOUTS, LAYOUT_CLASSNAMES } from '../../../video-layout/constants'; import { getCurrentLayout } from '../../../video-layout/functions.web'; import { FILMSTRIP_TYPE } from '../../constants'; -import { getScreenshareFilmstripParticipantId } from '../../functions'; +import { getScreenshareFilmstripParticipantId } from '../../functions.web'; import Filmstrip from './Filmstrip'; -type Props = { - - /** - * The current layout of the filmstrip. - */ - _currentLayout: string, +interface IProps { /** * The number of columns in tile view. */ - _columns: number, + _columns: number; /** - * The width of the filmstrip. + * The current layout of the filmstrip. */ - _filmstripWidth: number, + _currentLayout?: string; /** * The height of the filmstrip. */ - _filmstripHeight: number, + _filmstripHeight?: number; + + /** + * The width of the filmstrip. + */ + _filmstripWidth?: number; /** * Whether or not the current layout is vertical filmstrip. */ - _isVerticalFilmstrip: boolean, + _isVerticalFilmstrip: boolean; /** * The participants in the call. */ - _remoteParticipants: Array, + _remoteParticipants: Array; /** * The length of the remote participants array. */ - _remoteParticipantsLength: number, + _remoteParticipantsLength: number; /** * Whether or not the filmstrip should be user-resizable. */ - _resizableFilmstrip: boolean, + _resizableFilmstrip: boolean; /** * The number of rows in tile view. */ - _rows: number, + _rows: number; /** * The height of the thumbnail. */ - _thumbnailHeight: number, + _thumbnailHeight?: number; /** * The width of the thumbnail. */ - _thumbnailWidth: number, + _thumbnailWidth?: number; /** * Whether or not the vertical filmstrip should have a background color. */ - _verticalViewBackground: boolean, + _verticalViewBackground: boolean; /** * Whether or not the vertical filmstrip should be displayed as grid. */ - _verticalViewGrid: boolean, + _verticalViewGrid: boolean; /** * Additional CSS class names to add to the container of all the thumbnails. */ - _videosClassName: string -}; + _videosClassName: string; +} -const ScreenshareFilmstrip = (props: Props) => props._currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW - && props._remoteParticipants.length === 1 && ( - - - -); +// eslint-disable-next-line no-confusing-arrow +const ScreenshareFilmstrip = (props: IProps) => + props._currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW + && props._remoteParticipants.length === 1 ? ( + + + + ) : null +; /** * Maps (parts of) the Redux state to the associated {@code Filmstrip}'s props. * * @param {Object} state - The Redux state. + * @param {any} _ownProps - Components' own props. * @private - * @returns {Props} + * @returns {IProps} */ -function _mapStateToProps(state) { +function _mapStateToProps(state: IReduxState, _ownProps: any) { const { screenshareFilmstripDimensions: { filmstripHeight, diff --git a/react/features/filmstrip/components/web/StageFilmstrip.js b/react/features/filmstrip/components/web/StageFilmstrip.tsx similarity index 67% rename from react/features/filmstrip/components/web/StageFilmstrip.js rename to react/features/filmstrip/components/web/StageFilmstrip.tsx index 1ced187db4..70720237e0 100644 --- a/react/features/filmstrip/components/web/StageFilmstrip.js +++ b/react/features/filmstrip/components/web/StageFilmstrip.tsx @@ -1,7 +1,7 @@ -// @flow import React from 'react'; import { connect } from 'react-redux'; +import { IReduxState } from '../../../app/types'; import { getToolbarButtons } from '../../../base/config/functions.web'; import { isMobileBrowser } from '../../../base/environment/utils'; import { LAYOUTS, LAYOUT_CLASSNAMES } from '../../../video-layout/constants'; @@ -11,105 +11,109 @@ import { FILMSTRIP_TYPE, TOOLBAR_HEIGHT_MOBILE } from '../../constants'; -import { getActiveParticipantsIds } from '../../functions'; -import { isFilmstripResizable, isStageFilmstripTopPanel } from '../../functions.web'; +import { getActiveParticipantsIds, isFilmstripResizable, isStageFilmstripTopPanel } from '../../functions.web'; import Filmstrip from './Filmstrip'; -type Props = { - - /** - * The current layout of the filmstrip. - */ - _currentLayout: string, +interface IProps { /** * The number of columns in tile view. */ - _columns: number, + _columns: number; /** - * The width of the filmstrip. + * The current layout of the filmstrip. */ - _filmstripWidth: number, + _currentLayout?: string; /** * The height of the filmstrip. */ - _filmstripHeight: number, + _filmstripHeight?: number; + + /** + * The width of the filmstrip. + */ + _filmstripWidth?: number; /** * Whether or not the current layout is vertical filmstrip. */ - _isVerticalFilmstrip: boolean, + _isVerticalFilmstrip: boolean; /** * The participants in the call. */ - _remoteParticipants: Array, + _remoteParticipants: Array; /** * The length of the remote participants array. */ - _remoteParticipantsLength: number, + _remoteParticipantsLength: number; /** * Whether or not the filmstrip should be user-resizable. */ - _resizableFilmstrip: boolean, + _resizableFilmstrip: boolean; /** * The number of rows in tile view. */ - _rows: number, + _rows: number; /** * The height of the thumbnail. */ - _thumbnailHeight: number, + _thumbnailHeight?: number; /** * The width of the thumbnail. */ - _thumbnailWidth: number, + _thumbnailWidth?: number; /** * Whether or not the vertical filmstrip should have a background color. */ - _verticalViewBackground: boolean, + _verticalViewBackground: boolean; /** * Whether or not the vertical filmstrip should be displayed as grid. */ - _verticalViewGrid: boolean, + _verticalViewGrid: boolean; /** * Additional CSS class names to add to the container of all the thumbnails. */ - _videosClassName: string -}; + _videosClassName: string; +} -const StageFilmstrip = (props: Props) => props._currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW && ( - - - -); +// eslint-disable-next-line no-confusing-arrow +const StageFilmstrip = (props: IProps) => + props._currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW ? ( + + + + ) : null +; /** * Maps (parts of) the Redux state to the associated {@code Filmstrip}'s props. * * @param {Object} state - The Redux state. + * @param {any} _ownProps - Components' own props. * @private - * @returns {Props} + * @returns {IProps} */ -function _mapStateToProps(state) { +function _mapStateToProps(state: IReduxState, _ownProps: any) { const toolbarButtons = getToolbarButtons(state); const activeParticipants = getActiveParticipantsIds(state); const reduceHeight = state['features/toolbox'].visible && toolbarButtons.length; const { - gridDimensions: dimensions = {}, + gridDimensions: dimensions = { columns: undefined, + rows: undefined }, filmstripHeight, filmstripWidth, thumbnailSize @@ -117,7 +121,7 @@ function _mapStateToProps(state) { const gridDimensions = dimensions; const { clientHeight, clientWidth } = state['features/base/responsive-ui']; - const availableSpace = clientHeight - filmstripHeight; + const availableSpace = clientHeight - Number(filmstripHeight); let filmstripPadding = 0; if (availableSpace > 0) { @@ -134,17 +138,18 @@ function _mapStateToProps(state) { && isMobileBrowser() && clientWidth <= ASPECT_RATIO_BREAKPOINT; - const remoteFilmstripHeight = filmstripHeight - (collapseTileView && filmstripPadding > 0 ? filmstripPadding : 0); + const remoteFilmstripHeight = Number(filmstripHeight) - ( + collapseTileView && filmstripPadding > 0 ? filmstripPadding : 0); const _topPanelFilmstrip = isStageFilmstripTopPanel(state); return { - _columns: gridDimensions.columns, + _columns: gridDimensions.columns ?? 1, _currentLayout: getCurrentLayout(state), _filmstripHeight: remoteFilmstripHeight, _filmstripWidth: filmstripWidth, _remoteParticipants: activeParticipants, _resizableFilmstrip: isFilmstripResizable(state) && _topPanelFilmstrip, - _rows: gridDimensions.rows, + _rows: gridDimensions.rows ?? 1, _thumbnailWidth: thumbnailSize?.width, _thumbnailHeight: thumbnailSize?.height, _topPanelFilmstrip, diff --git a/react/features/invite/components/callee-info/CalleeInfo.js b/react/features/invite/components/callee-info/CalleeInfo.tsx similarity index 88% rename from react/features/invite/components/callee-info/CalleeInfo.js rename to react/features/invite/components/callee-info/CalleeInfo.tsx index 192ceeaf78..ed84946270 100644 --- a/react/features/invite/components/callee-info/CalleeInfo.js +++ b/react/features/invite/components/callee-info/CalleeInfo.tsx @@ -1,8 +1,7 @@ -// @flow - import React, { Component } from 'react'; import { connect } from 'react-redux'; +import { IReduxState } from '../../../app/types'; import Avatar from '../../../base/avatar/components/Avatar'; import { MEDIA_TYPE } from '../../../base/media/constants'; import { @@ -10,25 +9,29 @@ import { getParticipantPresenceStatus, getRemoteParticipants } from '../../../base/participants/functions'; -import { Container, Text } from '../../../base/react/components/index'; +import { Container, Text } from '../../../base/react/components/index.web'; import { isLocalTrackMuted } from '../../../base/tracks/functions.any'; import PresenceLabel from '../../../presence-status/components/PresenceLabel'; import { CALLING } from '../../../presence-status/constants'; -import styles from './styles'; +import styles from './styles.web'; /** * The type of the React {@code Component} props of {@link CalleeInfo}. */ -type Props = { +interface IProps { /** * The callee's information such as display name. */ - _callee: Object, + _callee?: { + id: string; + name: string; + status?: string; + }; - _isVideoMuted: boolean -}; + _isVideoMuted: boolean; +} /** @@ -37,7 +40,7 @@ type Props = { * * @augments Component */ -class CalleeInfo extends Component { +class CalleeInfo extends Component { /** * Implements React's {@link Component#render()}. * @@ -49,8 +52,8 @@ class CalleeInfo extends Component { id, name, status = CALLING - } = this.props._callee; - const className = this.props._isVideoMuted ? 'solidBG' : undefined; + } = this.props._callee ?? {}; + const className = this.props._isVideoMuted ? 'solidBG' : ''; return ( { * style: Object * }} */ - _style(...classNames: Array) { + _style(...classNames: Array) { let className = ''; - let style; + let style: Object = {}; for (const aClassName of classNames) { if (aClassName) { @@ -99,7 +102,7 @@ class CalleeInfo extends Component { // React Native will accept an Array as the value of the // style prop. However, I do not know about React. style = { - ...style, + ...style, // @ts-ignore ...styles[aClassName] }; } else { @@ -111,7 +114,10 @@ class CalleeInfo extends Component { // Choose which of the className and/or style props has a value and, // consequently, must be returned. - const props = {}; + const props = { + className: '', + style: {} + }; if (className) { props.className = className.trim(); @@ -133,7 +139,7 @@ class CalleeInfo extends Component { * _callee: Object * }} */ -function _mapStateToProps(state) { +function _mapStateToProps(state: IReduxState) { const _isVideoMuted = isLocalTrackMuted(state['features/base/tracks'], MEDIA_TYPE.VIDEO); diff --git a/react/features/invite/components/callee-info/CalleeInfoContainer.js b/react/features/invite/components/callee-info/CalleeInfoContainer.tsx similarity index 82% rename from react/features/invite/components/callee-info/CalleeInfoContainer.js rename to react/features/invite/components/callee-info/CalleeInfoContainer.tsx index b10dd1007d..8d17fffbaa 100644 --- a/react/features/invite/components/callee-info/CalleeInfoContainer.js +++ b/react/features/invite/components/callee-info/CalleeInfoContainer.tsx @@ -1,14 +1,14 @@ -// @flow - import React, { Component } from 'react'; import { connect } from 'react-redux'; +import { IReduxState } from '../../../app/types'; + import CalleeInfo from './CalleeInfo'; /** * The type of the React {@code Component} props of {@code CalleeInfoContainer}. */ -type Props = { +interface IProps { /** * The indicator which determines whether {@code CalleeInfo} is to be @@ -16,8 +16,8 @@ type Props = { * * @private */ - _calleeInfoVisible: boolean -}; + _calleeInfoVisible: boolean; +} /** * Implements a React {@link Component} which depicts the establishment of a @@ -25,7 +25,7 @@ type Props = { * * @augments Component */ -class CalleeInfoContainer extends Component { +class CalleeInfoContainer extends Component { /** * Implements React's {@link Component#render()}. * @@ -48,7 +48,7 @@ class CalleeInfoContainer extends Component { * _calleeInfoVisible: boolean * }} */ -function _mapStateToProps(state: Object) { +function _mapStateToProps(state: IReduxState) { return { /** * The indicator which determines whether {@code CalleeInfo} is to be @@ -57,7 +57,7 @@ function _mapStateToProps(state: Object) { * @private * @type {boolean} */ - _calleeInfoVisible: state['features/invite'].calleeInfoVisible + _calleeInfoVisible: Boolean(state['features/invite'].calleeInfoVisible) }; } diff --git a/react/features/invite/components/callee-info/styles.web.ts b/react/features/invite/components/callee-info/styles.web.ts new file mode 100644 index 0000000000..ff8b4c5632 --- /dev/null +++ b/react/features/invite/components/callee-info/styles.web.ts @@ -0,0 +1 @@ +export default {}; diff --git a/react/features/invite/components/dial-in-info-page/DialInInfoApp.web.js b/react/features/invite/components/dial-in-info-page/DialInInfoApp.web.tsx similarity index 86% rename from react/features/invite/components/dial-in-info-page/DialInInfoApp.web.js rename to react/features/invite/components/dial-in-info-page/DialInInfoApp.web.tsx index 6bc8721c11..21851f714e 100644 --- a/react/features/invite/components/dial-in-info-page/DialInInfoApp.web.js +++ b/react/features/invite/components/dial-in-info-page/DialInInfoApp.web.tsx @@ -6,11 +6,12 @@ import { isMobileBrowser } from '../../../base/environment/utils'; import i18next from '../../../base/i18n/i18next'; import { parseURLParams } from '../../../base/util/parseURLParams'; import { DIAL_IN_INFO_PAGE_PATH_NAME } from '../../constants'; -import { DialInSummary } from '../dial-in-summary'; +import DialInSummary from '../dial-in-summary/web/DialInSummary'; -import NoRoomError from './NoRoomError'; +import NoRoomError from './NoRoomError.web'; document.addEventListener('DOMContentLoaded', () => { + // @ts-ignore const { room } = parseURLParams(window.location, true, 'search'); const { href } = window.location; const ix = href.indexOf(DIAL_IN_INFO_PAGE_PATH_NAME); @@ -30,6 +31,6 @@ document.addEventListener('DOMContentLoaded', () => { ); }); -window.addEventListener('beforeunload', () => { +window.addEventListener('beforeunload', () => { // @ts-ignore ReactDOM.unmountComponentAtNode(document.getElementById('react')); }); diff --git a/react/features/invite/components/dial-in-info-page/NoRoomError.web.js b/react/features/invite/components/dial-in-info-page/NoRoomError.web.js deleted file mode 100644 index 99fa49e006..0000000000 --- a/react/features/invite/components/dial-in-info-page/NoRoomError.web.js +++ /dev/null @@ -1,48 +0,0 @@ -/* @flow */ - -import React, { Component } from 'react'; - -import { translate } from '../../../base/i18n/functions'; - -/** - * The type of the React {@code Component} props of {@link NoRoomError}. - */ -type Props = { - - /** - * Additional CSS classnames to append to the root of the component. - */ - className: string, - - /** - * Invoked to obtain translated strings. - */ - t: Function -}; - -/** - * Displays an error message stating no room name was specified to fetch dial-in - * numbers for. - * - * @augments Component - */ -class NoRoomError extends Component { - /** - * Implements React's {@link Component#render()}. - * - * @inheritdoc - * @returns {ReactElement} - */ - render() { - const { t } = this.props; - - return ( -
-
{ t('info.noNumbers') }
-
{ t('info.noRoom') }
-
- ); - } -} - -export default translate(NoRoomError); diff --git a/react/features/invite/components/dial-in-info-page/NoRoomError.web.tsx b/react/features/invite/components/dial-in-info-page/NoRoomError.web.tsx new file mode 100644 index 0000000000..9e82161d5e --- /dev/null +++ b/react/features/invite/components/dial-in-info-page/NoRoomError.web.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; + +/** + * The type of the React {@code Component} props of {@link NoRoomError}. + */ +interface IProps { + + /** + * Additional CSS classnames to append to the root of the component. + */ + className: string; +} + +const NoRoomError = ({ className }: IProps) => { + const { t } = useTranslation(); + + return ( +
+
{t('info.noNumbers')}
+
{t('info.noRoom')}
+
+ ); +}; + +export default NoRoomError; diff --git a/react/features/invite/components/dial-in-summary/web/DialInSummaryApp.js b/react/features/invite/components/dial-in-summary/web/DialInSummaryApp.tsx similarity index 90% rename from react/features/invite/components/dial-in-summary/web/DialInSummaryApp.js rename to react/features/invite/components/dial-in-summary/web/DialInSummaryApp.tsx index 5c72769490..00cd4f1dc2 100644 --- a/react/features/invite/components/dial-in-summary/web/DialInSummaryApp.js +++ b/react/features/invite/components/dial-in-summary/web/DialInSummaryApp.tsx @@ -1,7 +1,5 @@ -// @flow - import { AtlasKitThemeProvider } from '@atlaskit/theme'; -import React from 'react'; +import React, { ComponentType } from 'react'; import BaseApp from '../../../../base/app/components/BaseApp'; import { isMobileBrowser } from '../../../../base/environment/utils'; @@ -18,12 +16,7 @@ import DialInSummary from './DialInSummary'; * * @augments BaseApp */ -export default class DialInSummaryApp extends BaseApp { - /** - * The deferred for the initialisation {{promise, resolve, reject}}. - */ - _init: Object; - +export default class DialInSummaryApp extends BaseApp { /** * Navigates to {@link Prejoin} upon mount. * @@ -32,6 +25,7 @@ export default class DialInSummaryApp extends BaseApp { async componentDidMount() { await super.componentDidMount(); + // @ts-ignore const { room } = parseURLParams(window.location, true, 'search'); const { href } = window.location; const ix = href.indexOf(DIAL_IN_INFO_PAGE_PATH_NAME); @@ -58,7 +52,7 @@ export default class DialInSummaryApp extends BaseApp { * * @override */ - _createMainElement(component, props) { + _createMainElement(component: ComponentType, props: Object) { return ( diff --git a/react/features/invite/reducer.ts b/react/features/invite/reducer.ts index 620351a3a9..2eee8f87aa 100644 --- a/react/features/invite/reducer.ts +++ b/react/features/invite/reducer.ts @@ -29,7 +29,11 @@ export interface IInviteState { error?: { status: number; }; - initialCalleeInfo?: Object; + initialCalleeInfo?: { + id: string; + name: string; + status: string; + }; numbers?: string[]; numbersEnabled: boolean; numbersFetched: boolean; diff --git a/react/features/large-video/components/LargeVideo.web.js b/react/features/large-video/components/LargeVideo.web.tsx similarity index 81% rename from react/features/large-video/components/LargeVideo.web.js rename to react/features/large-video/components/LargeVideo.web.tsx index 7bb97787e4..ebb2adf870 100644 --- a/react/features/large-video/components/LargeVideo.web.js +++ b/react/features/large-video/components/LargeVideo.web.tsx @@ -1,9 +1,9 @@ -// @flow - import React, { Component } from 'react'; import { connect } from 'react-redux'; +// @ts-expect-error import VideoLayout from '../../../../modules/UI/videolayout/VideoLayout'; +import { IReduxState } from '../../app/types'; import { VIDEO_TYPE } from '../../base/media/constants'; import { getLocalParticipant } from '../../base/participants/functions'; import Watermarks from '../../base/react/components/web/Watermarks'; @@ -13,113 +13,111 @@ import { setColorAlpha } from '../../base/util/helpers'; import StageParticipantNameLabel from '../../display-name/components/web/StageParticipantNameLabel'; import { FILMSTRIP_BREAKPOINT } from '../../filmstrip/constants'; import { getVerticalViewMaxWidth, isFilmstripResizable } from '../../filmstrip/functions.web'; -import { getLargeVideoParticipant } from '../../large-video/functions'; import SharedVideo from '../../shared-video/components/web/SharedVideo'; import Captions from '../../subtitles/components/Captions.web'; -import { setTileView } from '../../video-layout/actions'; +import { setTileView } from '../../video-layout/actions.web'; import Whiteboard from '../../whiteboard/components/web/Whiteboard'; import { isWhiteboardEnabled } from '../../whiteboard/functions'; import { setSeeWhatIsBeingShared } from '../actions.web'; +import { getLargeVideoParticipant } from '../functions'; import ScreenSharePlaceholder from './ScreenSharePlaceholder.web'; // Hack to detect Spot. const SPOT_DISPLAY_NAME = 'Meeting Room'; -declare var interfaceConfig: Object; - -type Props = { +interface IProps { /** * The alpha(opacity) of the background. */ - _backgroundAlpha: number, + _backgroundAlpha?: number; /** * The user selected background color. */ - _customBackgroundColor: string, + _customBackgroundColor: string; /** * The user selected background image url. */ - _customBackgroundImageUrl: string, + _customBackgroundImageUrl: string; /** * Whether the screen-sharing placeholder should be displayed or not. */ - _displayScreenSharingPlaceholder: boolean, + _displayScreenSharingPlaceholder: boolean; + + /** + * Whether or not the hideSelfView is enabled. + */ + _hideSelfView: boolean; /** * Prop that indicates whether the chat is open. */ - _isChatOpen: boolean, - - /** - * Used to determine the value of the autoplay attribute of the underlying - * video element. - */ - _noAutoPlayVideo: boolean, - - /** - * Whether or not the filmstrip is resizable. - */ - _resizableFilmstrip: boolean, - - /** - * Whether or not to show dominant speaker badge. - */ - _showDominantSpeakerBadge: boolean, - - /** - * The width of the vertical filmstrip (user resized). - */ - _verticalFilmstripWidth: ?number, - - /** - * The max width of the vertical filmstrip. - */ - _verticalViewMaxWidth: number, - - /** - * Whether or not the filmstrip is visible. - */ - _visibleFilmstrip: boolean, - - /** - * The large video participant id. - */ - _largeVideoParticipantId: string, + _isChatOpen: boolean; /** * Whether or not the local screen share is on large-video. */ - _isScreenSharing: boolean, + _isScreenSharing: boolean; /** - * Whether or not the screen sharing is visible. + * The large video participant id. */ - _seeWhatIsBeingShared: boolean, - - /** - * Whether or not the whiteboard is enabled. - */ - _whiteboardEnabled: boolean; - - /** - * Whether or not the hideSelfView is enabled. - */ - _hideSelfView: boolean; + _largeVideoParticipantId: string; /** * Local Participant id. */ _localParticipantId: string; + /** + * Used to determine the value of the autoplay attribute of the underlying + * video element. + */ + _noAutoPlayVideo: boolean; + + /** + * Whether or not the filmstrip is resizable. + */ + _resizableFilmstrip: boolean; + + /** + * Whether or not the screen sharing is visible. + */ + _seeWhatIsBeingShared: boolean; + + /** + * Whether or not to show dominant speaker badge. + */ + _showDominantSpeakerBadge: boolean; + + /** + * The width of the vertical filmstrip (user resized). + */ + _verticalFilmstripWidth?: number | null; + + /** + * The max width of the vertical filmstrip. + */ + _verticalViewMaxWidth: number; + + /** + * Whether or not the filmstrip is visible. + */ + _visibleFilmstrip: boolean; + + /** + * Whether or not the whiteboard is enabled. + */ + _whiteboardEnabled: boolean; + /** * The Redux dispatch function. */ - dispatch: Function + dispatch: Function; } /** . @@ -128,23 +126,23 @@ type Props = { * * @augments Component */ -class LargeVideo extends Component { - _tappedTimeout: ?TimeoutID; +class LargeVideo extends Component { + _tappedTimeout: number | undefined; - _containerRef: Object; + _containerRef: React.RefObject; - _wrapperRef: Object; + _wrapperRef: React.RefObject; /** * Constructor of the component. * * @inheritdoc */ - constructor(props) { + constructor(props: IProps) { super(props); - this._containerRef = React.createRef(); - this._wrapperRef = React.createRef(); + this._containerRef = React.createRef(); + this._wrapperRef = React.createRef(); this._clearTapTimeout = this._clearTapTimeout.bind(this); this._onDoubleTap = this._onDoubleTap.bind(this); @@ -156,7 +154,7 @@ class LargeVideo extends Component { * * @inheritdoc */ - componentDidUpdate(prevProps: Props) { + componentDidUpdate(prevProps: IProps) { const { _visibleFilmstrip, _isScreenSharing, @@ -249,8 +247,6 @@ class LargeVideo extends Component { ); } - _updateLayout: () => void; - /** * Refreshes the video layout to determine the dimensions of the stage view. * If the filmstrip is toggled it adds CSS transition classes and removes them @@ -261,22 +257,20 @@ class LargeVideo extends Component { _updateLayout() { const { _verticalFilmstripWidth, _resizableFilmstrip } = this.props; - if (_resizableFilmstrip && _verticalFilmstripWidth >= FILMSTRIP_BREAKPOINT) { - this._containerRef.current.classList.add('transition'); - this._wrapperRef.current.classList.add('transition'); + if (_resizableFilmstrip && Number(_verticalFilmstripWidth) >= FILMSTRIP_BREAKPOINT) { + this._containerRef.current?.classList.add('transition'); + this._wrapperRef.current?.classList.add('transition'); VideoLayout.refreshLayout(); setTimeout(() => { - this._containerRef.current && this._containerRef.current.classList.remove('transition'); - this._wrapperRef.current && this._wrapperRef.current.classList.remove('transition'); + this._containerRef?.current && this._containerRef.current.classList.remove('transition'); + this._wrapperRef?.current && this._wrapperRef.current.classList.remove('transition'); }, 1000); } else { VideoLayout.refreshLayout(); } } - _clearTapTimeout: () => void; - /** * Clears the '_tappedTimout'. * @@ -295,7 +289,7 @@ class LargeVideo extends Component { * @returns {Object} */ _getCustomStyles() { - const styles = {}; + const styles: any = {}; const { _customBackgroundColor, _customBackgroundImageUrl, @@ -317,15 +311,13 @@ class LargeVideo extends Component { styles.backgroundSize = 'cover'; } - if (_visibleFilmstrip && _verticalFilmstripWidth >= FILMSTRIP_BREAKPOINT) { + if (_visibleFilmstrip && Number(_verticalFilmstripWidth) >= FILMSTRIP_BREAKPOINT) { styles.width = `calc(100% - ${_verticalViewMaxWidth || 0}px)`; } return styles; } - _onDoubleTap: () => void; - /** * Sets view to tile view on double tap. * @@ -333,7 +325,7 @@ class LargeVideo extends Component { * @private * @returns {void} */ - _onDoubleTap(e) { + _onDoubleTap(e: React.TouchEvent) { e.stopPropagation(); e.preventDefault(); @@ -341,7 +333,7 @@ class LargeVideo extends Component { this._clearTapTimeout(); this.props.dispatch(setTileView(true)); } else { - this._tappedTimeout = setTimeout(this._clearTapTimeout, 300); + this._tappedTimeout = window.setTimeout(this._clearTapTimeout, 300); } } } @@ -352,9 +344,9 @@ class LargeVideo extends Component { * * @param {Object} state - The Redux state. * @private - * @returns {Props} + * @returns {IProps} */ -function _mapStateToProps(state) { +function _mapStateToProps(state: IReduxState) { const testingConfig = state['features/base/config'].testing; const { backgroundColor, backgroundImageUrl } = state['features/dynamic-branding']; const { isOpen: isChatOpen } = state['features/chat']; @@ -364,7 +356,7 @@ function _mapStateToProps(state) { const localParticipantId = getLocalParticipant(state)?.id; const largeVideoParticipant = getLargeVideoParticipant(state); const videoTrack = getVideoTrackByParticipant(state, largeVideoParticipant); - const isLocalScreenshareOnLargeVideo = largeVideoParticipant?.id?.includes(localParticipantId) + const isLocalScreenshareOnLargeVideo = largeVideoParticipant?.id?.includes(localParticipantId ?? '') && videoTrack?.videoType === VIDEO_TYPE.DESKTOP; const isOnSpot = defaultLocalDisplayName === SPOT_DISPLAY_NAME; @@ -372,15 +364,15 @@ function _mapStateToProps(state) { _backgroundAlpha: state['features/base/config'].backgroundAlpha, _customBackgroundColor: backgroundColor, _customBackgroundImageUrl: backgroundImageUrl, - _displayScreenSharingPlaceholder: isLocalScreenshareOnLargeVideo && !seeWhatIsBeingShared && !isOnSpot, + _displayScreenSharingPlaceholder: Boolean(isLocalScreenshareOnLargeVideo && !seeWhatIsBeingShared && !isOnSpot), _hideSelfView: getHideSelfView(state), _isChatOpen: isChatOpen, - _isScreenSharing: isLocalScreenshareOnLargeVideo, - _largeVideoParticipantId: largeVideoParticipant?.id, - _localParticipantId: localParticipantId, - _noAutoPlayVideo: testingConfig?.noAutoPlayVideo, + _isScreenSharing: Boolean(isLocalScreenshareOnLargeVideo), + _largeVideoParticipantId: largeVideoParticipant?.id ?? '', + _localParticipantId: localParticipantId ?? '', + _noAutoPlayVideo: Boolean(testingConfig?.noAutoPlayVideo), _resizableFilmstrip: isFilmstripResizable(state), - _seeWhatIsBeingShared: seeWhatIsBeingShared, + _seeWhatIsBeingShared: Boolean(seeWhatIsBeingShared), _showDominantSpeakerBadge: !hideDominantSpeakerBadge, _verticalFilmstripWidth: verticalFilmstripWidth.current, _verticalViewMaxWidth: getVerticalViewMaxWidth(state), diff --git a/react/features/lobby/components/AbstractLobbyScreen.tsx b/react/features/lobby/components/AbstractLobbyScreen.tsx index 7e65d842f8..fd0e7d4744 100644 --- a/react/features/lobby/components/AbstractLobbyScreen.tsx +++ b/react/features/lobby/components/AbstractLobbyScreen.tsx @@ -10,6 +10,7 @@ import { getFeatureFlag } from '../../base/flags/functions'; import { getLocalParticipant } from '../../base/participants/functions'; import { getFieldValue } from '../../base/react/functions'; import { updateSettings } from '../../base/settings/actions'; +import { IMessage } from '../../chat/reducer'; import { isDeviceStatusVisible } from '../../prejoin/functions'; import { cancelKnocking, joinWithPassword, onSendMessage, setPasswordJoinFailed, startKnocking } from '../actions'; @@ -39,12 +40,12 @@ export interface IProps { /** * Lobby messages between moderator and the participant. */ - _lobbyChatMessages: Object; + _lobbyChatMessages: IMessage[]; /** * Name of the lobby chat recipient. */ - _lobbyMessageRecipient: string; + _lobbyMessageRecipient?: string; /** * The name of the meeting we're about to join. @@ -54,22 +55,22 @@ export interface IProps { /** * The members only conference if any,. */ - _membersOnlyConference: IJitsiConference; + _membersOnlyConference?: IJitsiConference; /** * The email of the participant about to knock/join. */ - _participantEmail: string; + _participantEmail?: string; /** * The id of the participant about to knock/join. This is the participant ID in the lobby room, at this point. */ - _participantId: string; + _participantId?: string; /** * The name of the participant about to knock/join. */ - _participantName: string; + _participantName?: string; /** * True if a recent attempt to join with password failed. @@ -373,42 +374,54 @@ export default class AbstractLobbyScreen

extends Pure * * @returns {React$Element} */ - _renderJoining: () => React.ReactElement; + _renderJoining() { + return <>; + } /** * Renders the participant form to let the knocking participant enter its details. * * @returns {React$Element} */ - _renderParticipantForm: () => React.ReactElement; + _renderParticipantForm() { + return <>; + } /** * Renders the participant info fragment when we have all the required details of the user. * * @returns {React$Element} */ - _renderParticipantInfo: () => React.ReactElement; + _renderParticipantInfo() { + return <>; + } /** * Renders the password form to let the participant join by using a password instead of knocking. * * @returns {React$Element} */ - _renderPasswordForm: () => React.ReactElement; + _renderPasswordForm() { + return <>; + } /** * Renders the password join button (set). * * @returns {React$Element} */ - _renderPasswordJoinButtons: () => React.ReactElement; + _renderPasswordJoinButtons() { + return <>; + } /** * Renders the standard (pre-knocking) button set. * * @returns {React$Element} */ - _renderStandardButtons: () => React.ReactElement; + _renderStandardButtons() { + return <>; + } } /** diff --git a/react/features/lobby/components/web/LobbyScreen.js b/react/features/lobby/components/web/LobbyScreen.tsx similarity index 91% rename from react/features/lobby/components/web/LobbyScreen.js rename to react/features/lobby/components/web/LobbyScreen.tsx index b65bd25900..9889545306 100644 --- a/react/features/lobby/components/web/LobbyScreen.js +++ b/react/features/lobby/components/web/LobbyScreen.tsx @@ -1,5 +1,3 @@ -// @flow - import React from 'react'; import { connect } from 'react-redux'; @@ -13,19 +11,19 @@ import Input from '../../../base/ui/components/web/Input'; import ChatInput from '../../../chat/components/web/ChatInput'; import MessageContainer from '../../../chat/components/web/MessageContainer'; import AbstractLobbyScreen, { - type Props, + IProps, _mapStateToProps } from '../AbstractLobbyScreen'; /** * Implements a waiting screen that represents the participant being in the lobby. */ -class LobbyScreen extends AbstractLobbyScreen { +class LobbyScreen extends AbstractLobbyScreen { /** * Reference to the React Component for displaying chat messages. Used for * scrolling to the end of the chat messages. */ - _messageContainerRef: Object; + _messageContainerRef: React.RefObject; /** * Initializes a new {@code LobbyScreen} instance. @@ -33,10 +31,10 @@ class LobbyScreen extends AbstractLobbyScreen { * @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._messageContainerRef = React.createRef(); + this._messageContainerRef = React.createRef(); } /** @@ -53,7 +51,7 @@ class LobbyScreen extends AbstractLobbyScreen { * * @inheritdoc */ - componentDidUpdate(prevProps) { + componentDidUpdate(prevProps: IProps) { if (this.props._lobbyChatMessages !== prevProps._lobbyChatMessages) { this._scrollMessageContainerToBottom(true); } else if (this.props._isLobbyChatActive && !prevProps._isLobbyChatActive) { @@ -80,34 +78,6 @@ class LobbyScreen extends AbstractLobbyScreen { ); } - _getScreenTitleKey: () => string; - - _onAskToJoin: () => boolean; - - _onCancel: () => boolean; - - _onChangeDisplayName: Object => void; - - _onChangeEmail: Object => void; - - _onChangePassword: Object => void; - - _onEnableEdit: () => void; - - _onJoinWithPassword: () => void; - - _onSendMessage: () => void; - - _onSubmit: () => boolean; - - _onSwitchToKnockMode: () => void; - - _onSwitchToPasswordMode: () => void; - - _onToggleChat: () => void; - - _renderContent: () => React$Element<*>; - /** * Renders the joining (waiting) fragment of the screen. * @@ -293,9 +263,9 @@ class LobbyScreen extends AbstractLobbyScreen { * @private * @returns {void} */ - _scrollMessageContainerToBottom(withAnimation) { + _scrollMessageContainerToBottom(withAnimation: boolean) { if (this._messageContainerRef.current) { - this._messageContainerRef.current.scrollToElement(withAnimation); + this._messageContainerRef.current.scrollToElement(withAnimation, null); } } } diff --git a/react/features/prejoin/components/web/PrejoinApp.js b/react/features/prejoin/components/web/PrejoinApp.tsx similarity index 81% rename from react/features/prejoin/components/web/PrejoinApp.js rename to react/features/prejoin/components/web/PrejoinApp.tsx index f616e3cc36..33631daf24 100644 --- a/react/features/prejoin/components/web/PrejoinApp.js +++ b/react/features/prejoin/components/web/PrejoinApp.tsx @@ -1,7 +1,5 @@ -// @flow - import { AtlasKitThemeProvider } from '@atlaskit/theme'; -import React from 'react'; +import React, { ComponentType } from 'react'; import { batch } from 'react-redux'; import BaseApp from '../../../base/app/components/BaseApp'; @@ -11,7 +9,7 @@ import { createPrejoinTracks } from '../../../base/tracks/functions.web'; import GlobalStyles from '../../../base/ui/components/GlobalStyles.web'; import JitsiThemeProvider from '../../../base/ui/components/JitsiThemeProvider.web'; import DialogContainer from '../../../base/ui/components/web/DialogContainer'; -import { initPrejoin, makePrecallTest } from '../../actions'; +import { initPrejoin, makePrecallTest } from '../../actions.web'; import PrejoinThirdParty from './PrejoinThirdParty'; @@ -20,8 +18,8 @@ type Props = { /** * Indicates the style type that needs to be applied. */ - styleType: string -} + styleType: string; +}; /** * Wrapper application for prejoin. @@ -29,10 +27,6 @@ type Props = { * @augments BaseApp */ export default class PrejoinApp extends BaseApp { - /** - * The deferred for the initialisation {{promise, resolve, reject}}. - */ - _init: Object; /** * Navigates to {@link Prejoin} upon mount. @@ -43,7 +37,7 @@ export default class PrejoinApp extends BaseApp { await super.componentDidMount(); const { store } = this.state; - const { dispatch } = store; + const { dispatch } = store ?? {}; const { styleType } = this.props; super._navigate({ @@ -53,9 +47,12 @@ export default class PrejoinApp extends BaseApp { } }); - const { startWithAudioMuted, startWithVideoMuted } = store.getState()['features/base/settings']; + const { startWithAudioMuted, startWithVideoMuted } = store + ? store.getState()['features/base/settings'] + : { startWithAudioMuted: undefined, + startWithVideoMuted: undefined }; - dispatch(setConfig({ + dispatch?.(setConfig({ prejoinConfig: { enabled: true }, @@ -68,8 +65,8 @@ export default class PrejoinApp extends BaseApp { const tracks = await tryCreateLocalTracks; batch(() => { - dispatch(initPrejoin(tracks, errors)); - dispatch(makePrecallTest(getConferenceOptions(store.getState()))); + dispatch?.(initPrejoin(tracks, errors)); + store && dispatch?.(makePrecallTest(getConferenceOptions(store.getState()))); }); } @@ -79,7 +76,7 @@ export default class PrejoinApp extends BaseApp { * * @override */ - _createMainElement(component, props) { + _createMainElement(component: ComponentType, props: Object) { return ( diff --git a/react/features/prejoin/components/web/PrejoinThirdParty.js b/react/features/prejoin/components/web/PrejoinThirdParty.tsx similarity index 84% rename from react/features/prejoin/components/web/PrejoinThirdParty.js rename to react/features/prejoin/components/web/PrejoinThirdParty.tsx index c8bfa86a04..f6f5f990b3 100644 --- a/react/features/prejoin/components/web/PrejoinThirdParty.js +++ b/react/features/prejoin/components/web/PrejoinThirdParty.tsx @@ -1,47 +1,42 @@ -// @flow - import React, { Component } from 'react'; +import { WithTranslation } from 'react-i18next'; import { connect } from 'react-redux'; +import { IReduxState } from '../../../app/types'; import { translate } from '../../../base/i18n/functions'; import { isVideoMutedByUser } from '../../../base/media/functions'; import PreMeetingScreen from '../../../base/premeeting/components/web/PreMeetingScreen'; import { getLocalJitsiVideoTrack } from '../../../base/tracks/functions.web'; import { isDeviceStatusVisible } from '../../functions'; -type Props = { +interface IProps extends WithTranslation { /** * Indicates the className that needs to be applied. */ - className: string, + className: string; /** * Flag signaling if the device status is visible or not. */ - deviceStatusVisible: boolean, + deviceStatusVisible: boolean; /** * Flag signaling the visibility of camera preview. */ - showCameraPreview: boolean, - - /** - * Used for translation. - */ - t: Function, + showCameraPreview: boolean; /** * The JitsiLocalTrack to display. */ - videoTrack: ?Object -}; + videoTrack?: Object; +} /** * This component is displayed before joining a meeting. */ -class PrejoinThirdParty extends Component { +class PrejoinThirdParty extends Component { /** * Implements React's {@link Component#render()}. * @@ -75,7 +70,7 @@ class PrejoinThirdParty extends Component { * @param {Object} ownProps - The props passed to the component. * @returns {Object} */ -function mapStateToProps(state) { +function mapStateToProps(state: IReduxState) { return { deviceStatusVisible: isDeviceStatusVisible(state), showCameraPreview: !isVideoMutedByUser(state), diff --git a/react/features/recent-list/components/AbstractRecentList.tsx b/react/features/recent-list/components/AbstractRecentList.tsx index 371b461fc8..9086cbe6e0 100644 --- a/react/features/recent-list/components/AbstractRecentList.tsx +++ b/react/features/recent-list/components/AbstractRecentList.tsx @@ -8,7 +8,7 @@ import AbstractPage from '../../base/react/components/AbstractPage'; import { Container, Text } from '../../base/react/components/index'; // @ts-ignore -import styles from './styles.web'; +import styles from './styles'; /** * The type of the React {@code Component} props of {@link AbstractRecentList}. diff --git a/react/features/recording/components/AbstractRecordingLabel.ts b/react/features/recording/components/AbstractRecordingLabel.ts index 74da7310bd..2b61c27a56 100644 --- a/react/features/recording/components/AbstractRecordingLabel.ts +++ b/react/features/recording/components/AbstractRecordingLabel.ts @@ -192,7 +192,7 @@ export default class AbstractRecordingLabel * _status: ?string * }} */ -export function _mapStateToProps(state: IReduxState, ownProps: IProps) { +export function _mapStateToProps(state: IReduxState, ownProps: any) { const { mode } = ownProps; return { diff --git a/react/features/shared-video/components/web/SharedVideo.js b/react/features/shared-video/components/web/SharedVideo.tsx similarity index 88% rename from react/features/shared-video/components/web/SharedVideo.js rename to react/features/shared-video/components/web/SharedVideo.tsx index cad755789f..1ecbc23603 100644 --- a/react/features/shared-video/components/web/SharedVideo.js +++ b/react/features/shared-video/components/web/SharedVideo.tsx @@ -1,54 +1,55 @@ -// @flow - import React, { Component } from 'react'; import { connect } from 'react-redux'; +// @ts-expect-error import Filmstrip from '../../../../../modules/UI/videolayout/Filmstrip'; +import { IReduxState } from '../../../app/types'; import { getLocalParticipant } from '../../../base/participants/functions'; import { getVerticalViewMaxWidth } from '../../../filmstrip/functions.web'; import { getToolboxHeight } from '../../../toolbox/functions.web'; +// @ts-ignore import VideoManager from './VideoManager'; +// eslint-disable-next-line lines-around-comment +// @ts-ignore import YoutubeVideoManager from './YoutubeVideoManager'; -declare var interfaceConfig: Object; - -type Props = { +interface IProps { /** * The available client width. */ - clientHeight: number, + clientHeight: number; /** * The available client width. */ - clientWidth: number, + clientWidth: number; /** * Whether the (vertical) filmstrip is visible or not. */ - filmstripVisible: boolean, + filmstripVisible: boolean; /** * The width of the vertical filmstrip. */ - filmstripWidth: number, + filmstripWidth: number; /** * Is the video shared by the local user. */ - isOwner: boolean, + isOwner: boolean; /** * Whether or not the user is actively resizing the filmstrip. */ - isResizing: boolean, + isResizing: boolean; /** * The shared video url. */ - videoUrl: string, + videoUrl?: string; } /** . @@ -57,7 +58,7 @@ type Props = { * * @augments Component */ -class SharedVideo extends Component { +class SharedVideo extends Component { /** * Computes the width and the height of the component. * @@ -140,9 +141,9 @@ class SharedVideo extends Component { * * @param {Object} state - The Redux state. * @private - * @returns {Props} + * @returns {IProps} */ -function _mapStateToProps(state) { +function _mapStateToProps(state: IReduxState) { const { ownerId, videoUrl } = state['features/shared-video']; const { clientHeight, clientWidth } = state['features/base/responsive-ui']; const { visible, isResizing } = state['features/filmstrip']; diff --git a/react/features/subtitles/components/AbstractCaptions.ts b/react/features/subtitles/components/AbstractCaptions.tsx similarity index 85% rename from react/features/subtitles/components/AbstractCaptions.ts rename to react/features/subtitles/components/AbstractCaptions.tsx index 24cb96738b..8da07872fc 100644 --- a/react/features/subtitles/components/AbstractCaptions.ts +++ b/react/features/subtitles/components/AbstractCaptions.tsx @@ -1,4 +1,4 @@ -import { Component } from 'react'; +import React, { Component } from 'react'; import { IReduxState } from '../../app/types'; @@ -17,7 +17,7 @@ export type AbstractCaptionsProps = { * Mapped by id just to have the keys for convenience during the rendering * process. */ - _transcripts: Map; + _transcripts?: Map; }; /** @@ -42,7 +42,7 @@ export class AbstractCaptions

const paragraphs = []; - for (const [ id, text ] of _transcripts) { + for (const [ id, text ] of _transcripts ?? []) { paragraphs.push(this._renderParagraph(id, text)); } @@ -53,25 +53,29 @@ export class AbstractCaptions

* Renders the transcription text. * * @abstract - * @param {string} id - The ID of the transcript message from which the + * @param {string} _id - The ID of the transcript message from which the * {@code text} has been created. - * @param {string} text - Subtitles text formatted with the participant's + * @param {string} _text - Subtitles text formatted with the participant's * name. * @protected * @returns {React$Element} - The React element which displays the text. */ - _renderParagraph: (id: string, text: string) => React.ReactElement; + _renderParagraph(_id: string, _text: string) { + return <>; + } /** * Renders the subtitles container. * * @abstract - * @param {Array} paragraphs - An array of elements created + * @param {Array} _el - An array of elements created * for each subtitle using the {@link _renderParagraph} method. * @protected * @returns {React$Element} - The subtitles container. */ - _renderSubtitlesContainer: (el: Array) => React.ReactElement; + _renderSubtitlesContainer(_el: Array) { + return <>; + } } /** diff --git a/react/features/subtitles/components/Captions.web.js b/react/features/subtitles/components/Captions.web.tsx similarity index 81% rename from react/features/subtitles/components/Captions.web.js rename to react/features/subtitles/components/Captions.web.tsx index 9d0ae5c5c1..737504d066 100644 --- a/react/features/subtitles/components/Captions.web.js +++ b/react/features/subtitles/components/Captions.web.tsx @@ -1,8 +1,7 @@ -// @flow - import React from 'react'; import { connect } from 'react-redux'; +import { IReduxState } from '../../app/types'; import { getLocalParticipant } from '../../base/participants/functions'; import { getLargeVideoParticipant } from '../../large-video/functions'; import { isLayoutTileView } from '../../video-layout/functions.web'; @@ -13,20 +12,19 @@ import { _abstractMapStateToProps } from './AbstractCaptions'; -type Props = { +interface IProps extends AbstractCaptionsProps { /** * Whether the subtitles container is lifted above the invite box. */ - _isLifted: boolean -} & AbstractCaptionsProps; + _isLifted: boolean; +} /** * React {@code Component} which can display speech-to-text results from * Jigasi as subtitles. */ -class Captions - extends AbstractCaptions { +class Captions extends AbstractCaptions { /** * Renders the transcription text. @@ -38,7 +36,7 @@ class Captions * @protected * @returns {React$Element} - The React element which displays the text. */ - _renderParagraph(id: string, text: string): React$Element<*> { + _renderParagraph(id: string, text: string) { return (

{ text } @@ -54,8 +52,7 @@ class Captions * @protected * @returns {React$Element} - The subtitles container. */ - _renderSubtitlesContainer( - paragraphs: Array>): React$Element<*> { + _renderSubtitlesContainer(paragraphs: Array) { const className = this.props._isLifted ? 'transcription-subtitles lifted' : 'transcription-subtitles'; @@ -75,14 +72,14 @@ class Captions * @private * @returns {Object} */ -function mapStateToProps(state) { +function mapStateToProps(state: IReduxState) { const isTileView = isLayoutTileView(state); const largeVideoParticipant = getLargeVideoParticipant(state); const localParticipant = getLocalParticipant(state); return { ..._abstractMapStateToProps(state), - _isLifted: largeVideoParticipant && largeVideoParticipant?.id !== localParticipant?.id && !isTileView + _isLifted: Boolean(largeVideoParticipant && largeVideoParticipant?.id !== localParticipant?.id && !isTileView) }; } diff --git a/react/features/toolbox/components/web/Toolbox.tsx b/react/features/toolbox/components/web/Toolbox.tsx index 07fc1e1bb8..5ac9db9774 100644 --- a/react/features/toolbox/components/web/Toolbox.tsx +++ b/react/features/toolbox/components/web/Toolbox.tsx @@ -1508,7 +1508,7 @@ class Toolbox extends Component { * @private * @returns {{}} */ -function _mapStateToProps(state: IReduxState, ownProps: Partial) { +function _mapStateToProps(state: IReduxState, ownProps: any) { const { conference } = state['features/base/conference']; const { isNarrowLayout } = state['features/base/responsive-ui']; const endConferenceSupported = conference?.isEndConferenceSupported() && isLocalParticipantModerator(state); diff --git a/react/features/invite/components/callee-info/styles.web.js b/react/features/transcribing/components/TranscribingExpandedLabel.web.ts similarity index 100% rename from react/features/invite/components/callee-info/styles.web.js rename to react/features/transcribing/components/TranscribingExpandedLabel.web.ts diff --git a/react/features/video-quality/components/VideoQualityExpandedLabel.web.js b/react/features/video-quality/components/VideoQualityExpandedLabel.web.js deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/react/features/transcribing/components/TranscribingExpandedLabel.web.js b/react/features/video-quality/components/VideoQualityExpandedLabel.web.ts similarity index 100% rename from react/features/transcribing/components/TranscribingExpandedLabel.web.js rename to react/features/video-quality/components/VideoQualityExpandedLabel.web.ts diff --git a/react/features/video-quality/components/VideoQualityLabel.web.js b/react/features/video-quality/components/VideoQualityLabel.web.tsx similarity index 92% rename from react/features/video-quality/components/VideoQualityLabel.web.js rename to react/features/video-quality/components/VideoQualityLabel.web.tsx index 245d34e351..2a5085e28a 100644 --- a/react/features/video-quality/components/VideoQualityLabel.web.js +++ b/react/features/video-quality/components/VideoQualityLabel.web.tsx @@ -1,8 +1,7 @@ -// @flow - import React from 'react'; import { connect } from 'react-redux'; +import { IReduxState, IStore } from '../../app/types'; import { openDialog } from '../../base/dialog/actions'; import { translate } from '../../base/i18n/functions'; import { IconPerformance } from '../../base/icons/svg'; @@ -17,25 +16,23 @@ import AbstractVideoQualityLabel, { } from './AbstractVideoQualityLabel'; import VideoQualityDialog from './VideoQualityDialog.web'; -declare var interfaceConfig: Object; - -type Props = AbstractProps & { - - /** - * The redux dispatch function. - */ - dispatch: Function, +interface IProps extends AbstractProps { /** * The message to show within the label's tooltip. */ - _tooltipKey: string, + _tooltipKey: string; /** * Flag controlling visibility of the component. */ - _visible: boolean, -}; + _visible: boolean; + + /** + * The redux dispatch function. + */ + dispatch: IStore['dispatch']; +} /** * React {@code Component} responsible for displaying a label that indicates @@ -44,7 +41,7 @@ type Props = AbstractProps & { * Will display if not in audio only mode and a high-definition large video is * being displayed. */ -export class VideoQualityLabel extends AbstractVideoQualityLabel { +export class VideoQualityLabel extends AbstractVideoQualityLabel { /** * Implements React's {@link Component#render()}. @@ -108,7 +105,7 @@ export class VideoQualityLabel extends AbstractVideoQualityLabel { * _visible: boolean * }} */ -function _mapStateToProps(state) { +function _mapStateToProps(state: IReduxState) { return { ..._abstractMapStateToProps(state), _visible: !(shouldDisplayTileView(state) || interfaceConfig.VIDEO_QUALITY_LABEL_DISABLED) diff --git a/react/features/visitors/components/web/VisitorsCountLabel.tsx b/react/features/visitors/components/web/VisitorsCountLabel.tsx index a9079f037c..f78876ffe4 100644 --- a/react/features/visitors/components/web/VisitorsCountLabel.tsx +++ b/react/features/visitors/components/web/VisitorsCountLabel.tsx @@ -25,7 +25,7 @@ const VisitorsCountLabel = () => { state['features/visitors'].count || 0); const { t } = useTranslation(); - return !visitorsMode && visitorsCount > 0 && ( 0 ? ( ); + ) : null; }; export default VisitorsCountLabel;