ref(TS) Convert always-on-top to TS (#13332)

This commit is contained in:
Robert Pintilii
2023-05-12 10:28:50 +03:00
committed by GitHub
parent 71627f97f7
commit ae0669fa07
10 changed files with 65 additions and 93 deletions

1
globals.d.ts vendored
View File

@@ -21,6 +21,7 @@ declare global {
JitsiMeetElectron?: any; JitsiMeetElectron?: any;
// selenium tests handler // selenium tests handler
_sharedVideoPlayer: any; _sharedVideoPlayer: any;
alwaysOnTop: { api: any };
} }
interface Document { interface Document {

View File

@@ -1,5 +1,3 @@
// @flow
import React, { Component } from 'react'; import React, { Component } from 'react';
// We need to reference these files directly to avoid loading things that are not available // We need to reference these files directly to avoid loading things that are not available
@@ -19,15 +17,15 @@ const TOOLBAR_TIMEOUT = 4000;
/** /**
* The type of the React {@code Component} state of {@link AlwaysOnTop}. * The type of the React {@code Component} state of {@link AlwaysOnTop}.
*/ */
type State = { interface IState {
avatarURL: string, avatarURL: string;
customAvatarBackgrounds: Array<string>, customAvatarBackgrounds: Array<string>;
displayName: string, displayName: string;
formattedDisplayName: string, formattedDisplayName: string;
isVideoDisplayed: boolean, isVideoDisplayed: boolean;
userID: string, userID: string;
visible: boolean visible: boolean;
}; }
/** /**
* Represents the always on top page. * Represents the always on top page.
@@ -35,7 +33,7 @@ type State = {
* @class AlwaysOnTop * @class AlwaysOnTop
* @augments Component * @augments Component
*/ */
export default class AlwaysOnTop extends Component<*, State> { export default class AlwaysOnTop extends Component<any, IState> {
_hovered: boolean; _hovered: boolean;
/** /**
@@ -44,7 +42,7 @@ export default class AlwaysOnTop extends Component<*, State> {
* @param {*} props - The read-only properties with which the new instance * @param {*} props - The read-only properties with which the new instance
* is to be initialized. * is to be initialized.
*/ */
constructor(props: *) { constructor(props: any) {
super(props); super(props);
this.state = { this.state = {
@@ -68,28 +66,25 @@ export default class AlwaysOnTop extends Component<*, State> {
this._onMouseOver = this._onMouseOver.bind(this); this._onMouseOver = this._onMouseOver.bind(this);
} }
_avatarChangedListener: () => void;
/** /**
* Handles avatar changed api events. * Handles avatar changed api events.
* *
* @returns {void} * @returns {void}
*/ */
_avatarChangedListener({ avatarURL, id }) { _avatarChangedListener({ avatarURL, id }: { avatarURL: string; id: string; }) {
if (api._getOnStageParticipant() === id if (api._getOnStageParticipant() === id
&& avatarURL !== this.state.avatarURL) { && avatarURL !== this.state.avatarURL) {
this.setState({ avatarURL }); this.setState({ avatarURL });
} }
} }
_displayNameChangedListener: () => void;
/** /**
* Handles display name changed api events. * Handles display name changed api events.
* *
* @returns {void} * @returns {void}
*/ */
_displayNameChangedListener({ displayname, formattedDisplayName, id }) { _displayNameChangedListener({ displayname, formattedDisplayName, id }: { displayname: string;
formattedDisplayName: string; id: string; }) {
if (api._getOnStageParticipant() === id if (api._getOnStageParticipant() === id
&& (formattedDisplayName !== this.state.formattedDisplayName && (formattedDisplayName !== this.state.formattedDisplayName
|| displayname !== this.state.displayName)) { || displayname !== this.state.displayName)) {
@@ -118,8 +113,6 @@ export default class AlwaysOnTop extends Component<*, State> {
TOOLBAR_TIMEOUT); TOOLBAR_TIMEOUT);
} }
_videoChangedListener: () => void;
/** /**
* Handles large video changed api events. * Handles large video changed api events.
* *
@@ -141,8 +134,6 @@ export default class AlwaysOnTop extends Component<*, State> {
}); });
} }
_mouseMove: () => void;
/** /**
* Handles mouse move events. * Handles mouse move events.
* *
@@ -152,8 +143,6 @@ export default class AlwaysOnTop extends Component<*, State> {
this.state.visible || this.setState({ visible: true }); this.state.visible || this.setState({ visible: true });
} }
_onMouseOut: () => void;
/** /**
* Toolbar mouse out handler. * Toolbar mouse out handler.
* *
@@ -163,8 +152,6 @@ export default class AlwaysOnTop extends Component<*, State> {
this._hovered = false; this._hovered = false;
} }
_onMouseOver: () => void;
/** /**
* Toolbar mouse over handler. * Toolbar mouse over handler.
* *
@@ -229,7 +216,7 @@ export default class AlwaysOnTop extends Component<*, State> {
this._hideToolbarAfterTimeout(); this._hideToolbarAfterTimeout();
api.getCustomAvatarBackgrounds() api.getCustomAvatarBackgrounds()
.then(res => .then((res: { avatarBackgrounds?: string[]; }) =>
this.setState({ this.setState({
customAvatarBackgrounds: res.avatarBackgrounds || [] customAvatarBackgrounds: res.avatarBackgrounds || []
})) }))
@@ -242,7 +229,7 @@ export default class AlwaysOnTop extends Component<*, State> {
* @inheritdoc * @inheritdoc
* @returns {void} * @returns {void}
*/ */
componentDidUpdate(prevProps: *, prevState: State) { componentDidUpdate(_prevProps: any, prevState: IState) {
if (!prevState.visible && this.state.visible) { if (!prevState.visible && this.state.visible) {
this._hideToolbarAfterTimeout(); this._hideToolbarAfterTimeout();
} }

View File

@@ -1,11 +1,9 @@
// @flow
import React, { Component } from 'react'; import React, { Component } from 'react';
// We need to reference these files directly to avoid loading things that are not available // We need to reference these files directly to avoid loading things that are not available
// in this environment (e.g. JitsiMeetJS or interfaceConfig) // in this environment (e.g. JitsiMeetJS or interfaceConfig)
import { IconMic, IconMicSlash } from '../base/icons/svg'; import { IconMic, IconMicSlash } from '../base/icons/svg';
import type { Props } from '../base/toolbox/components/AbstractButton'; import { IProps } from '../base/toolbox/components/AbstractButton';
import ToolbarButton from './ToolbarButton'; import ToolbarButton from './ToolbarButton';
@@ -14,23 +12,25 @@ const { api } = window.alwaysOnTop;
/** /**
* The type of the React {@code Component} state of {@link AudioMuteButton}. * The type of the React {@code Component} state of {@link AudioMuteButton}.
*/ */
type State = { interface IState {
/** /**
* Whether audio is available is not. * Whether audio is available is not.
*/ */
audioAvailable: boolean, audioAvailable: boolean;
/** /**
* Whether audio is muted or not. * Whether audio is muted or not.
*/ */
audioMuted: boolean audioMuted: boolean;
}; }
type Props = Partial<IProps>;
/** /**
* Stateless "mute/unmute audio" button for the Always-on-Top windows. * Stateless "mute/unmute audio" button for the Always-on-Top windows.
*/ */
export default class AudioMuteButton extends Component<Props, State> { export default class AudioMuteButton extends Component<Props, IState> {
icon = IconMic; icon = IconMic;
toggledIcon = IconMicSlash; toggledIcon = IconMicSlash;
accessibilityLabel = 'Audio mute'; accessibilityLabel = 'Audio mute';
@@ -38,7 +38,7 @@ export default class AudioMuteButton extends Component<Props, State> {
/** /**
* Initializes a new {@code AudioMuteButton} instance. * Initializes a new {@code AudioMuteButton} instance.
* *
* @param {Props} props - The React {@code Component} props to initialize * @param {IProps} props - The React {@code Component} props to initialize
* the new {@code AudioMuteButton} instance with. * the new {@code AudioMuteButton} instance with.
*/ */
constructor(props: Props) { constructor(props: Props) {
@@ -94,27 +94,23 @@ export default class AudioMuteButton extends Component<Props, State> {
this._audioMutedListener); this._audioMutedListener);
} }
_audioAvailabilityListener: ({ available: boolean }) => void;
/** /**
* Handles audio available api events. * Handles audio available api events.
* *
* @param {{ available: boolean }} status - The new available status. * @param {{ available: boolean }} status - The new available status.
* @returns {void} * @returns {void}
*/ */
_audioAvailabilityListener({ available }) { _audioAvailabilityListener({ available }: { available: boolean; }) {
this.setState({ audioAvailable: available }); this.setState({ audioAvailable: available });
} }
_audioMutedListener: ({ muted: boolean }) => void;
/** /**
* Handles audio muted api events. * Handles audio muted api events.
* *
* @param {{ muted: boolean }} status - The new muted status. * @param {{ muted: boolean }} status - The new muted status.
* @returns {void} * @returns {void}
*/ */
_audioMutedListener({ muted }) { _audioMutedListener({ muted }: { muted: boolean; }) {
this.setState({ audioMuted: muted }); this.setState({ audioMuted: muted });
} }
@@ -144,16 +140,14 @@ export default class AudioMuteButton extends Component<Props, State> {
* Changes the muted state. * Changes the muted state.
* *
* @override * @override
* @param {boolean} audioMuted - Whether audio should be muted or not. * @param {boolean} _audioMuted - Whether audio should be muted or not.
* @protected * @protected
* @returns {void} * @returns {void}
*/ */
_setAudioMuted(audioMuted: boolean) { // eslint-disable-line no-unused-vars _setAudioMuted(_audioMuted: boolean) {
this.state.audioAvailable && api.executeCommand('toggleAudio'); this.state.audioAvailable && api.executeCommand('toggleAudio');
} }
_onClick: () => {};
/** /**
* Handles clicking / pressing the button, and toggles the audio mute state * Handles clicking / pressing the button, and toggles the audio mute state
* accordingly. * accordingly.

View File

@@ -1,19 +1,20 @@
// @flow
import React, { Component } from 'react'; import React, { Component } from 'react';
// We need to reference these files directly to avoid loading things that are not available // We need to reference these files directly to avoid loading things that are not available
// in this environment (e.g. JitsiMeetJS or interfaceConfig) // in this environment (e.g. JitsiMeetJS or interfaceConfig)
import { IconHangup } from '../base/icons/svg'; import { IconHangup } from '../base/icons/svg';
import type { Props } from '../base/toolbox/components/AbstractButton'; import { IProps } from '../base/toolbox/components/AbstractButton';
import ToolbarButton from './ToolbarButton'; import ToolbarButton from './ToolbarButton';
const { api } = window.alwaysOnTop; const { api } = window.alwaysOnTop;
type Props = Partial<IProps>;
/** /**
* Stateless hangup button for the Always-on-Top windows. * Stateless hangup button for the Always-on-Top windows.
*/ */
export default class HangupButton extends Component<Props, *> { export default class HangupButton extends Component<Props> {
accessibilityLabel = 'Hangup'; accessibilityLabel = 'Hangup';
icon = IconHangup; icon = IconHangup;
@@ -21,7 +22,7 @@ export default class HangupButton extends Component<Props, *> {
/** /**
* Initializes a new {@code HangupButton} instance. * Initializes a new {@code HangupButton} instance.
* *
* @param {Props} props - The React {@code Component} props to initialize * @param {IProps} props - The React {@code Component} props to initialize
* the new {@code HangupButton} instance with. * the new {@code HangupButton} instance with.
*/ */
constructor(props: Props) { constructor(props: Props) {
@@ -31,8 +32,6 @@ export default class HangupButton extends Component<Props, *> {
this._onClick = this._onClick.bind(this); this._onClick = this._onClick.bind(this);
} }
_onClick: () => {};
/** /**
* Handles clicking / pressing the button, and disconnects the conference. * Handles clicking / pressing the button, and disconnects the conference.
* *

View File

@@ -1,5 +1,3 @@
// @flow
import React, { Component } from 'react'; import React, { Component } from 'react';
import AudioMuteButton from './AudioMuteButton'; import AudioMuteButton from './AudioMuteButton';
@@ -9,30 +7,30 @@ import VideoMuteButton from './VideoMuteButton';
/** /**
* The type of the React {@code Component} props of {@link Toolbar}. * The type of the React {@code Component} props of {@link Toolbar}.
*/ */
type Props = { interface IProps {
/** /**
* Additional CSS class names to add to the root of the toolbar. * Additional CSS class names to add to the root of the toolbar.
*/ */
className: string, className: string;
/** /**
* Callback invoked when no longer moused over the toolbar. * Callback invoked when no longer moused over the toolbar.
*/ */
onMouseOut: Function, onMouseOut: (e?: React.MouseEvent) => void;
/** /**
* Callback invoked when the mouse has moved over the toolbar. * Callback invoked when the mouse has moved over the toolbar.
*/ */
onMouseOver: Function onMouseOver: (e?: React.MouseEvent) => void;
}; }
/** /**
* Represents the toolbar in the Always On Top window. * Represents the toolbar in the Always On Top window.
* *
* @augments Component * @augments Component
*/ */
export default class Toolbar extends Component<Props> { export default class Toolbar extends Component<IProps> {
/** /**
* Implements React's {@link Component#render()}. * Implements React's {@link Component#render()}.
* *

View File

@@ -2,38 +2,38 @@ import React, { useCallback } from 'react';
import Icon from '../base/icons/components/Icon'; import Icon from '../base/icons/components/Icon';
type Props = { interface IProps {
/** /**
* Accessibility label for button. * Accessibility label for button.
*/ */
accessibilityLabel: string, accessibilityLabel: string;
/** /**
* An extra class name to be added at the end of the element's class name * An extra class name to be added at the end of the element's class name
* in order to enable custom styling. * in order to enable custom styling.
*/ */
customClass?: string, customClass?: string;
/** /**
* Whether or not the button is disabled. * Whether or not the button is disabled.
*/ */
disabled?: boolean, disabled?: boolean;
/**
* Click handler.
*/
onClick: Function,
/** /**
* Button icon. * Button icon.
*/ */
icon: Object, icon: Function;
/**
* Click handler.
*/
onClick: (e?: React.MouseEvent) => void;
/** /**
* Whether or not the button is toggled. * Whether or not the button is toggled.
*/ */
toggled?: boolean toggled?: boolean;
} }
const ToolbarButton = ({ const ToolbarButton = ({
@@ -43,7 +43,7 @@ const ToolbarButton = ({
onClick, onClick,
icon, icon,
toggled = false toggled = false
}: Props) => { }: IProps) => {
const onKeyPress = useCallback(event => { const onKeyPress = useCallback(event => {
if (event.key === 'Enter' || event.key === ' ') { if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault(); event.preventDefault();

View File

@@ -1,15 +1,16 @@
// @flow
import React, { Component } from 'react'; import React, { Component } from 'react';
// We need to reference these files directly to avoid loading things that are not available // We need to reference these files directly to avoid loading things that are not available
// in this environment (e.g. JitsiMeetJS or interfaceConfig) // in this environment (e.g. JitsiMeetJS or interfaceConfig)
import { IconVideo, IconVideoOff } from '../base/icons/svg'; import { IconVideo, IconVideoOff } from '../base/icons/svg';
import type { Props } from '../base/toolbox/components/AbstractButton'; import { IProps } from '../base/toolbox/components/AbstractButton';
import ToolbarButton from './ToolbarButton'; import ToolbarButton from './ToolbarButton';
const { api } = window.alwaysOnTop; const { api } = window.alwaysOnTop;
type Props = Partial<IProps>;
/** /**
* The type of the React {@code Component} state of {@link VideoMuteButton}. * The type of the React {@code Component} state of {@link VideoMuteButton}.
*/ */
@@ -18,12 +19,12 @@ type State = {
/** /**
* Whether video is available is not. * Whether video is available is not.
*/ */
videoAvailable: boolean, videoAvailable: boolean;
/** /**
* Whether video is muted or not. * Whether video is muted or not.
*/ */
videoMuted: boolean videoMuted: boolean;
}; };
/** /**
@@ -119,40 +120,34 @@ export default class VideoMuteButton extends Component<Props, State> {
* Changes the muted state. * Changes the muted state.
* *
* @override * @override
* @param {boolean} videoMuted - Whether video should be muted or not. * @param {boolean} _videoMuted - Whether video should be muted or not.
* @protected * @protected
* @returns {void} * @returns {void}
*/ */
_setVideoMuted(videoMuted: boolean) { // eslint-disable-line no-unused-vars _setVideoMuted(_videoMuted: boolean) {
this.state.videoAvailable && api.executeCommand('toggleVideo', false, true); this.state.videoAvailable && api.executeCommand('toggleVideo', false, true);
} }
_videoAvailabilityListener: ({ available: boolean }) => void;
/** /**
* Handles video available api events. * Handles video available api events.
* *
* @param {{ available: boolean }} status - The new available status. * @param {{ available: boolean }} status - The new available status.
* @returns {void} * @returns {void}
*/ */
_videoAvailabilityListener({ available }) { _videoAvailabilityListener({ available }: { available: boolean; }) {
this.setState({ videoAvailable: available }); this.setState({ videoAvailable: available });
} }
_videoMutedListener: ({ muted: boolean }) => void;
/** /**
* Handles video muted api events. * Handles video muted api events.
* *
* @param {{ muted: boolean }} status - The new muted status. * @param {{ muted: boolean }} status - The new muted status.
* @returns {void} * @returns {void}
*/ */
_videoMutedListener({ muted }) { _videoMutedListener({ muted }: { muted: boolean; }) {
this.setState({ videoMuted: muted }); this.setState({ videoMuted: muted });
} }
_onClick: () => {};
/** /**
* Handles clicking / pressing the button, and toggles the video mute state * Handles clicking / pressing the button, and toggles the video mute state
* accordingly. * accordingly.

View File

@@ -1,14 +1,11 @@
// @flow
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import AlwaysOnTop from './AlwaysOnTop'; import AlwaysOnTop from './AlwaysOnTop';
// Render the main/root Component. // Render the main/root Component.
// $FlowExpectedError
ReactDOM.render(<AlwaysOnTop />, document.getElementById('react')); ReactDOM.render(<AlwaysOnTop />, document.getElementById('react'));
window.addEventListener( window.addEventListener(
'beforeunload', 'beforeunload',
() => ReactDOM.unmountComponentAtNode(document.getElementById('react'))); () => ReactDOM.unmountComponentAtNode(document.getElementById('react') ?? document.body));

View File

@@ -16,6 +16,7 @@
}, },
"exclude": [ "exclude": [
"node_modules", "node_modules",
"react/features/always-on-top",
"react/features/analytics/handlers/GoogleAnalyticsHandler.ts", "react/features/analytics/handlers/GoogleAnalyticsHandler.ts",
"react/features/base/components/participants-pane-list", "react/features/base/components/participants-pane-list",
"react/features/base/tooltip", "react/features/base/tooltip",

View File

@@ -299,7 +299,7 @@ module.exports = (_env, argv) => {
}), }),
Object.assign({}, config, { Object.assign({}, config, {
entry: { entry: {
'alwaysontop': './react/features/always-on-top/index.js' 'alwaysontop': './react/features/always-on-top/index.tsx'
}, },
plugins: [ plugins: [
...config.plugins, ...config.plugins,