From a52f9313a6961748e945b0b3cd73db28d2dbb3e4 Mon Sep 17 00:00:00 2001 From: Robert Pintilii Date: Wed, 29 Mar 2023 12:54:56 +0300 Subject: [PATCH] ref(TS Convert all Abstract classes to TS (#13127) --- .../{AbstractApp.js => AbstractApp.ts} | 23 +++--- .../components/{BaseApp.js => BaseApp.tsx} | 63 +++++++++------ react/features/base/config/configType.ts | 3 + react/features/base/util/helpers.ts | 2 +- ...utton.js => AbstractGoogleSignInButton.ts} | 18 ++--- ...leDialog.js => AbstractAddPeopleDialog.ts} | 81 ++++++++++--------- react/features/invite/functions.ts | 3 +- react/features/invite/types.ts | 1 + react/features/notifications/types.ts | 2 +- ...yLabel.js => AbstractVideoQualityLabel.ts} | 20 ++--- .../components/VideoQualityLabel.native.js | 2 +- .../components/VideoQualityLabel.web.js | 2 +- 12 files changed, 114 insertions(+), 106 deletions(-) rename react/features/app/components/{AbstractApp.js => AbstractApp.ts} (84%) rename react/features/base/app/components/{BaseApp.js => BaseApp.tsx} (83%) rename react/features/google-api/components/{AbstractGoogleSignInButton.js => AbstractGoogleSignInButton.ts} (74%) rename react/features/invite/components/add-people-dialog/{AbstractAddPeopleDialog.js => AbstractAddPeopleDialog.ts} (85%) rename react/features/video-quality/components/{AbstractVideoQualityLabel.js => AbstractVideoQualityLabel.ts} (61%) diff --git a/react/features/app/components/AbstractApp.js b/react/features/app/components/AbstractApp.ts similarity index 84% rename from react/features/app/components/AbstractApp.js rename to react/features/app/components/AbstractApp.ts index eced400df9..d96bf02b62 100644 --- a/react/features/app/components/AbstractApp.js +++ b/react/features/app/components/AbstractApp.ts @@ -1,7 +1,5 @@ -import React from 'react'; - -import { BaseApp } from '../../base/app'; -import { toURLString } from '../../base/util'; +import BaseApp from '../../base/app/components/BaseApp'; +import { toURLString } from '../../base/util/uri'; import { appNavigate } from '../actions'; import { getDefaultURL } from '../functions'; @@ -14,12 +12,12 @@ export type Props = { * XXX Refer to the implementation of loadURLObject: in * ios/sdk/src/JitsiMeetView.m for further information. */ - timestamp: any, + timestamp: any; /** * The URL, if any, with which the app was launched. */ - url: Object | string + url: Object | string; }; /** @@ -27,11 +25,13 @@ export type Props = { * * @abstract */ -export class AbstractApp extends BaseApp { +export class AbstractApp extends BaseApp { /** * The deferred for the initialisation {{promise, resolve, reject}}. */ - _init: Object; + _init: { + promise: Promise; + }; /** * Initializes the app. @@ -70,8 +70,6 @@ export class AbstractApp extends BaseApp { } } - _createMainElement: (React.ReactElement, Object) => ?React.ReactElement; - /** * Gets the default URL to be opened when this {@code App} mounts. * @@ -80,6 +78,7 @@ export class AbstractApp extends BaseApp { * mounts. */ _getDefaultURL() { + // @ts-ignore return getDefaultURL(this.state.store); } @@ -91,7 +90,7 @@ export class AbstractApp extends BaseApp { * @protected * @returns {void} */ - _openURL(url) { - this.state.store.dispatch(appNavigate(toURLString(url))); + _openURL(url: string | Object) { + this.state.store?.dispatch(appNavigate(toURLString(url))); } } diff --git a/react/features/base/app/components/BaseApp.js b/react/features/base/app/components/BaseApp.tsx similarity index 83% rename from react/features/base/app/components/BaseApp.js rename to react/features/base/app/components/BaseApp.tsx index a88447c0cf..edd05c2a60 100644 --- a/react/features/base/app/components/BaseApp.js +++ b/react/features/base/app/components/BaseApp.tsx @@ -1,27 +1,25 @@ -// @flow - +// @ts-expect-error import { jitsiLocalStorage } from '@jitsi/js-utils'; import _ from 'lodash'; -import React, { Component, Fragment } from 'react'; +import React, { Component, ComponentType, Fragment } from 'react'; import { I18nextProvider } from 'react-i18next'; import { Provider } from 'react-redux'; import { compose, createStore } from 'redux'; import Thunk from 'redux-thunk'; -import { i18next } from '../../i18n'; -import { - MiddlewareRegistry, - PersistenceRegistry, - ReducerRegistry, - StateListenerRegistry -} from '../../redux'; +import { IStore } from '../../../app/types'; +import i18next from '../../i18n/i18next'; +import MiddlewareRegistry from '../../redux/MiddlewareRegistry'; +import PersistenceRegistry from '../../redux/PersistenceRegistry'; +import ReducerRegistry from '../../redux/ReducerRegistry'; +import StateListenerRegistry from '../../redux/StateListenerRegistry'; +// eslint-disable-next-line lines-around-comment +// @ts-ignore import { SoundCollection } from '../../sounds'; -import { createDeferred } from '../../util'; +import { createDeferred } from '../../util/helpers'; import { appWillMount, appWillUnmount } from '../actions'; import logger from '../logger'; -declare var APP: Object; - /** * The type of the React {@code Component} state of {@link BaseApp}. */ @@ -30,12 +28,15 @@ type State = { /** * The {@code Route} rendered by the {@code BaseApp}. */ - route: Object, + route: { + component?: ComponentType; + props?: Object; + }; /** * The redux store used by the {@code BaseApp}. */ - store: Object + store?: IStore; }; /** @@ -43,11 +44,13 @@ type State = { * * @abstract */ -export default class BaseApp extends Component<*, State> { +export default class BaseApp

extends Component { /** * The deferred for the initialisation {{promise, resolve, reject}}. */ - _init: Object; + _init: { + promise: Promise; + }; /** * Initializes a new {@code BaseApp} instance. @@ -55,7 +58,7 @@ export default class BaseApp extends Component<*, State> { * @param {Object} props - The read-only React {@code Component} props with * which the new instance is to be initialized. */ - constructor(props: Object) { + constructor(props: P) { super(props); this.state = { @@ -85,6 +88,7 @@ export default class BaseApp extends Component<*, State> { const setStatePromise = new Promise(resolve => { this.setState({ + // @ts-ignore store: this._createStore() }, resolve); }); @@ -97,8 +101,9 @@ export default class BaseApp extends Component<*, State> { logger.error(err); } - this.state.store.dispatch(appWillMount(this)); + this.state.store?.dispatch(appWillMount(this)); + // @ts-ignore this._init.resolve(); } @@ -108,7 +113,7 @@ export default class BaseApp extends Component<*, State> { * @inheritdoc */ componentWillUnmount() { - this.state.store.dispatch(appWillUnmount(this)); + this.state.store?.dispatch(appWillUnmount(this)); } /** @@ -132,7 +137,7 @@ export default class BaseApp extends Component<*, State> { * @private * @returns {Promise} */ - _initStorage(): Promise<*> { + _initStorage(): Promise { const _initializing = jitsiLocalStorage.getItem('_initializing'); return _initializing || Promise.resolve(); @@ -159,6 +164,7 @@ export default class BaseApp extends Component<*, State> { if (store) { return ( + {/* @ts-ignore */} { this._createMainElement(component, props) } @@ -198,7 +204,7 @@ export default class BaseApp extends Component<*, State> { * @returns {ReactElement} * @protected */ - _createMainElement(component, props) { + _createMainElement(component?: ComponentType, props?: Object) { return component ? React.createElement(component, props || {}) : null; } @@ -219,6 +225,8 @@ export default class BaseApp extends Component<*, State> { // - Thunk - allows us to dispatch async actions easily. For more info // @see https://github.com/gaearon/redux-thunk. const middleware = MiddlewareRegistry.applyMiddleware(Thunk); + + // @ts-ignore const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; const store = createStore(reducer, PersistenceRegistry.getPersistedState(), composeEnhancers(middleware)); @@ -230,6 +238,7 @@ export default class BaseApp extends Component<*, State> { // Don't use in the react code!!! // FIXME: remove when the reactification is finished! if (typeof APP !== 'undefined') { + // @ts-ignore APP.store = store; } @@ -242,7 +251,11 @@ export default class BaseApp extends Component<*, State> { * @param {Route} route - The Route to which to navigate. * @returns {Promise} */ - _navigate(route): Promise<*> { + _navigate(route: { + component?: ComponentType; + href?: string; + props?: Object; + }): Promise { if (_.isEqual(route, this.state.route)) { return Promise.resolve(); } @@ -259,7 +272,7 @@ export default class BaseApp extends Component<*, State> { // performed before setState completes, the app may not navigate to the // expected route. In order to mitigate the problem, _navigate was // changed to return a Promise. - return new Promise(resolve => { + return new Promise(resolve => { // @ts-ignore this.setState({ route }, resolve); }); } @@ -269,5 +282,5 @@ export default class BaseApp extends Component<*, State> { * * @returns {React$Element} */ - _renderDialogContainer: () => React$Element<*>; + _renderDialogContainer: () => React.ReactElement; } diff --git a/react/features/base/config/configType.ts b/react/features/base/config/configType.ts index 1ed5ea4154..a5b3298d9f 100644 --- a/react/features/base/config/configType.ts +++ b/react/features/base/config/configType.ts @@ -236,6 +236,8 @@ export interface IConfig { }; dialInConfCodeUrl?: string; dialInNumbersUrl?: string; + dialOutAuthUrl?: string; + dialOutRegionUrl?: string; disable1On1Mode?: boolean | null; disableAddingBackgroundImages?: boolean; disableAudioLevels?: boolean; @@ -450,6 +452,7 @@ export interface IConfig { hideMuteAllButton?: boolean; }; pcStatsInterval?: number; + peopleSearchQueryTypes?: string[]; peopleSearchUrl?: string; preferredTranscribeLanguage?: string; prejoinConfig?: { diff --git a/react/features/base/util/helpers.ts b/react/features/base/util/helpers.ts index 84ac81e163..270e287edf 100644 --- a/react/features/base/util/helpers.ts +++ b/react/features/base/util/helpers.ts @@ -28,7 +28,7 @@ export function assignIfDefined(target: Object, source: Object) { * * @returns {{promise, resolve, reject}} */ -export function createDeferred(): Object { +export function createDeferred() { const deferred: any = {}; deferred.promise = new Promise((resolve, reject) => { diff --git a/react/features/google-api/components/AbstractGoogleSignInButton.js b/react/features/google-api/components/AbstractGoogleSignInButton.ts similarity index 74% rename from react/features/google-api/components/AbstractGoogleSignInButton.js rename to react/features/google-api/components/AbstractGoogleSignInButton.ts index 05d8e21947..f54dade5e0 100644 --- a/react/features/google-api/components/AbstractGoogleSignInButton.js +++ b/react/features/google-api/components/AbstractGoogleSignInButton.ts @@ -1,28 +1,22 @@ -// @flow - import { Component } from 'react'; +import { WithTranslation } from 'react-i18next'; /** * {@code AbstractGoogleSignInButton} Component's property types. */ -type Props = { +interface IProps extends WithTranslation { /** * The callback to invoke when the button is clicked. */ - onClick: Function, + onClick: Function; /** * True if the user is signed in, so it needs to render a different label * and maybe different style (for the future). */ - signedIn?: boolean, - - /** - * Function to be used to translate i18n labels. - */ - t: Function -}; + signedIn?: boolean; +} /** * Abstract class of the {@code GoogleSignInButton} to share platform @@ -30,5 +24,5 @@ type Props = { * * @inheritdoc */ -export default class AbstractGoogleSignInButton extends Component { +export default class AbstractGoogleSignInButton extends Component { } diff --git a/react/features/invite/components/add-people-dialog/AbstractAddPeopleDialog.js b/react/features/invite/components/add-people-dialog/AbstractAddPeopleDialog.ts similarity index 85% rename from react/features/invite/components/add-people-dialog/AbstractAddPeopleDialog.js rename to react/features/invite/components/add-people-dialog/AbstractAddPeopleDialog.ts index 3c2c4c4dcc..e9cd17ac14 100644 --- a/react/features/invite/components/add-people-dialog/AbstractAddPeopleDialog.js +++ b/react/features/invite/components/add-people-dialog/AbstractAddPeopleDialog.ts @@ -1,15 +1,15 @@ -// @flow - import { Component } from 'react'; -import { createInviteDialogEvent, sendAnalytics } from '../../../analytics'; -import { - NOTIFICATION_TIMEOUT_TYPE, - showNotification -} from '../../../notifications'; +import { createInviteDialogEvent } from '../../../analytics/AnalyticsEvents'; +import { sendAnalytics } from '../../../analytics/functions'; +import { IReduxState } from '../../../app/types'; +import { showNotification } from '../../../notifications/actions'; +import { NOTIFICATION_TIMEOUT_TYPE } from '../../../notifications/constants'; +import { INotificationProps } from '../../../notifications/types'; import { invite } from '../../actions'; import { INVITE_TYPES } from '../../constants'; import { + GetInviteResultsOptions, getInviteResultsForQuery, getInviteTypeCounts, isAddPeopleEnabled, @@ -17,83 +17,86 @@ import { isSipInviteEnabled } from '../../functions'; import logger from '../../logger'; +import { IInvitee } from '../../types'; -export type Props = { +export interface IProps { /** * Whether or not to show Add People functionality. */ - _addPeopleEnabled: boolean, + _addPeopleEnabled: boolean; + + _appId: string; /** * Whether or not call flows are enabled. */ - _callFlowsEnabled: boolean, + _callFlowsEnabled: boolean; /** * The URL for validating if a phone number can be called. */ - _dialOutAuthUrl: string, + _dialOutAuthUrl: string; + + /** + * Whether or not to show Dial Out functionality. + */ + _dialOutEnabled: boolean; /** * The URL for validating if an outbound destination is allowed. */ _dialOutRegionUrl: string; - /** - * Whether or not to show Dial Out functionality. - */ - _dialOutEnabled: boolean, - - /** - * Whether or not to allow sip invites. - */ - _sipInviteEnabled: boolean, - /** * The JWT token. */ - _jwt: string, + _jwt: string; /** * The query types used when searching people. */ - _peopleSearchQueryTypes: Array, + _peopleSearchQueryTypes: Array; /** * The URL pointing to the service allowing for people search. */ - _peopleSearchUrl: string, + _peopleSearchUrl: string; + + /** + * Whether or not to allow sip invites. + */ + _sipInviteEnabled: boolean; /** * The Redux dispatch function. */ - dispatch: Function -}; + dispatch: Function; +} -export type State = { +export interface IState { /** * Indicating that an error occurred when adding people to the call. */ - addToCallError: boolean, + addToCallError: boolean; /** * Indicating that we're currently adding the new people to the * call. */ - addToCallInProgress: boolean, + addToCallInProgress: boolean; /** * The list of invite items. */ - inviteItems: Array, -}; + inviteItems: Array; +} /** * Implements an abstract dialog to invite people to the conference. */ -export default class AbstractAddPeopleDialog +export default class AbstractAddPeopleDialog

extends Component { /** * Constructor of the component. @@ -112,7 +115,7 @@ export default class AbstractAddPeopleDialog * @param {Object} invitee - The invitee object. * @returns {string} */ - _getDisplayName(invitee) { + _getDisplayName(invitee: IInvitee) { if (invitee.type === INVITE_TYPES.PHONE) { return invitee.number; } @@ -135,7 +138,7 @@ export default class AbstractAddPeopleDialog * @param {Array} invitees - The items to be invited. * @returns {Promise>} */ - _invite(invitees) { + _invite(invitees: IInvitee[]) { const inviteTypeCounts = getInviteTypeCounts(invitees); sendAnalytics(createInviteDialogEvent( @@ -155,7 +158,7 @@ export default class AbstractAddPeopleDialog const { _callFlowsEnabled, dispatch } = this.props; return dispatch(invite(invitees)) - .then(invitesLeftToSend => { + .then((invitesLeftToSend: IInvitee[]) => { this.setState({ addToCallInProgress: false }); @@ -179,7 +182,7 @@ export default class AbstractAddPeopleDialog }); } else if (!_callFlowsEnabled) { const invitedCount = invitees.length; - let notificationProps; + let notificationProps: INotificationProps | undefined; if (invitedCount >= 3) { notificationProps = { @@ -228,8 +231,6 @@ export default class AbstractAddPeopleDialog || this.state.addToCallInProgress; } - _query: (?string) => Promise>; - /** * Performs a people and phone number search request. * @@ -249,7 +250,7 @@ export default class AbstractAddPeopleDialog _peopleSearchUrl: peopleSearchUrl, _sipInviteEnabled: sipInviteEnabled } = this.props; - const options = { + const options: GetInviteResultsOptions = { addPeopleEnabled, appId, dialOutAuthUrl, @@ -280,7 +281,7 @@ export default class AbstractAddPeopleDialog * _peopleSearchUrl: string * }} */ -export function _mapStateToProps(state: Object) { +export function _mapStateToProps(state: IReduxState) { const { callFlowsEnabled, dialOutAuthUrl, diff --git a/react/features/invite/functions.ts b/react/features/invite/functions.ts index 46bdaea6bc..01c596b6e7 100644 --- a/react/features/invite/functions.ts +++ b/react/features/invite/functions.ts @@ -26,6 +26,7 @@ import { UPGRADE_OPTIONS_TEXT } from './constants'; import logger from './logger'; +import { IInvitee } from './types'; declare let $: any; @@ -379,7 +380,7 @@ export function getInviteText({ * @returns {Object} An object with keys as user types and values as the number * of invites for that type. */ -export function getInviteTypeCounts(inviteItems: Array<{ type: string; }> = []) { +export function getInviteTypeCounts(inviteItems: IInvitee[] = []) { const inviteTypeCounts: any = {}; inviteItems.forEach(({ type }) => { diff --git a/react/features/invite/types.ts b/react/features/invite/types.ts index a97711ee2c..2f592ce920 100644 --- a/react/features/invite/types.ts +++ b/react/features/invite/types.ts @@ -1,5 +1,6 @@ export interface IInvitee { address: string; + name: string; number: string; type: string; } diff --git a/react/features/notifications/types.ts b/react/features/notifications/types.ts index 6a170d57be..a60148bd75 100644 --- a/react/features/notifications/types.ts +++ b/react/features/notifications/types.ts @@ -15,7 +15,7 @@ export interface INotificationProps { sticky?: boolean; title?: string; titleArguments?: { - [key: string]: string; + [key: string]: string | number; }; titleKey?: string; uid?: string; diff --git a/react/features/video-quality/components/AbstractVideoQualityLabel.js b/react/features/video-quality/components/AbstractVideoQualityLabel.ts similarity index 61% rename from react/features/video-quality/components/AbstractVideoQualityLabel.js rename to react/features/video-quality/components/AbstractVideoQualityLabel.ts index 363d3af4ca..9b88d50569 100644 --- a/react/features/video-quality/components/AbstractVideoQualityLabel.js +++ b/react/features/video-quality/components/AbstractVideoQualityLabel.ts @@ -1,24 +1,20 @@ -// @flow - import { Component } from 'react'; +import { WithTranslation } from 'react-i18next'; -export type Props = { +import { IReduxState } from '../../app/types'; + +export interface IProps extends WithTranslation { /** * Whether or not the conference is in audio only mode. */ - _audioOnly: boolean, - - /** - * Invoked to obtain translated strings. - */ - t: Function -}; + _audioOnly: boolean; +} /** * Abstract class for the {@code VideoQualityLabel} component. */ -export default class AbstractVideoQualityLabel extends Component

{ +export default class AbstractVideoQualityLabel

extends Component

{ } @@ -32,7 +28,7 @@ export default class AbstractVideoQualityLabel extends Component

{ * _audioOnly: boolean * }} */ -export function _abstractMapStateToProps(state: Object) { +export function _abstractMapStateToProps(state: IReduxState) { const { enabled: audioOnly } = state['features/base/audio-only']; return { diff --git a/react/features/video-quality/components/VideoQualityLabel.native.js b/react/features/video-quality/components/VideoQualityLabel.native.js index 92ceb96762..9b5112de91 100644 --- a/react/features/video-quality/components/VideoQualityLabel.native.js +++ b/react/features/video-quality/components/VideoQualityLabel.native.js @@ -8,7 +8,7 @@ import { Label } from '../../base/label'; import { type StyleType, combineStyles } from '../../base/styles'; import AbstractVideoQualityLabel, { - type Props as AbstractProps, + IProps as AbstractProps, _abstractMapStateToProps } from './AbstractVideoQualityLabel'; import styles from './styles'; diff --git a/react/features/video-quality/components/VideoQualityLabel.web.js b/react/features/video-quality/components/VideoQualityLabel.web.js index 6ad17b309c..b57dd1d69a 100644 --- a/react/features/video-quality/components/VideoQualityLabel.web.js +++ b/react/features/video-quality/components/VideoQualityLabel.web.js @@ -12,7 +12,7 @@ import Tooltip from '../../base/tooltip/components/Tooltip'; import { shouldDisplayTileView } from '../../video-layout'; import AbstractVideoQualityLabel, { - type Props as AbstractProps, + IProps as AbstractProps, _abstractMapStateToProps } from './AbstractVideoQualityLabel'; import VideoQualityDialog from './VideoQualityDialog.web';