mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2025-12-30 11:22:31 +00:00
ref(TS) Convert some components to TS (#13137)
*Remove unnecessary @ts-ignores
This commit is contained in:
7
globals.d.ts
vendored
7
globals.d.ts
vendored
@@ -25,6 +25,13 @@ declare global {
|
||||
interfaceConfig?: any;
|
||||
JitsiMeetJS?: any;
|
||||
JitsiMeetElectron?: any;
|
||||
// selenium tests handler
|
||||
_sharedVideoPlayer: any;
|
||||
}
|
||||
|
||||
interface Document {
|
||||
mozCancelFullScreen?: Function;
|
||||
webkitExitFullscreen?: Function;
|
||||
}
|
||||
|
||||
const config: IConfig;
|
||||
|
||||
13
package-lock.json
generated
13
package-lock.json
generated
@@ -143,6 +143,7 @@
|
||||
"@types/react-native": "0.68.9",
|
||||
"@types/react-redux": "7.1.24",
|
||||
"@types/react-window": "1.8.5",
|
||||
"@types/resemblejs": "^4.1.0",
|
||||
"@types/unorm": "1.3.28",
|
||||
"@types/uuid": "8.3.4",
|
||||
"@types/zxcvbn": "4.4.1",
|
||||
@@ -6398,6 +6399,12 @@
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/resemblejs": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/resemblejs/-/resemblejs-4.1.0.tgz",
|
||||
"integrity": "sha512-+MIkKy/UngDfhTnvn2yK/KSzlbtLeB5BU73qqZrzIF24+e2r8enJ4cW3UbtkstByYSDV8pbheGAqg7zT8ZZ2pA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/retry": {
|
||||
"version": "0.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz",
|
||||
@@ -24951,6 +24958,12 @@
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"@types/resemblejs": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/resemblejs/-/resemblejs-4.1.0.tgz",
|
||||
"integrity": "sha512-+MIkKy/UngDfhTnvn2yK/KSzlbtLeB5BU73qqZrzIF24+e2r8enJ4cW3UbtkstByYSDV8pbheGAqg7zT8ZZ2pA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/retry": {
|
||||
"version": "0.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz",
|
||||
|
||||
@@ -148,6 +148,7 @@
|
||||
"@types/react-native": "0.68.9",
|
||||
"@types/react-redux": "7.1.24",
|
||||
"@types/react-window": "1.8.5",
|
||||
"@types/resemblejs": "^4.1.0",
|
||||
"@types/unorm": "1.3.28",
|
||||
"@types/uuid": "8.3.4",
|
||||
"@types/zxcvbn": "4.4.1",
|
||||
|
||||
@@ -164,10 +164,10 @@ export function appNavigate(uri?: string, options: IReloadNowOptions = {}) {
|
||||
* If we have a close page enabled, redirect to it without
|
||||
* showing any other dialog.
|
||||
*
|
||||
* @param {Object} options - Ignored.
|
||||
* @param {Object} _options - Ignored.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function maybeRedirectToWelcomePage(options: any) { // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
export function maybeRedirectToWelcomePage(_options?: any): any {
|
||||
// Dummy.
|
||||
}
|
||||
|
||||
|
||||
@@ -127,6 +127,7 @@ export interface INoiseSuppressionConfig {
|
||||
|
||||
export interface IConfig {
|
||||
_desktopSharingSourceDevice?: string;
|
||||
_screenshotHistoryRegionUrl?: string;
|
||||
analytics?: {
|
||||
amplitudeAPPKey?: string;
|
||||
disabled?: boolean;
|
||||
|
||||
@@ -1,79 +1,75 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { Icon } from '../../../icons';
|
||||
import Icon from '../../../icons/components/Icon';
|
||||
import Popover from '../../../popover/components/Popover.web';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Whether the element popup is expanded.
|
||||
*/
|
||||
ariaExpanded?: boolean,
|
||||
interface IProps {
|
||||
|
||||
/**
|
||||
* The id of the element this button icon controls.
|
||||
*/
|
||||
ariaControls?: string,
|
||||
ariaControls?: string;
|
||||
|
||||
/**
|
||||
* Whether the element popup is expanded.
|
||||
*/
|
||||
ariaExpanded?: boolean;
|
||||
|
||||
/**
|
||||
* Whether the element has a popup.
|
||||
*/
|
||||
ariaHasPopup?: boolean,
|
||||
ariaHasPopup?: boolean;
|
||||
|
||||
/**
|
||||
* Aria label for the Icon.
|
||||
*/
|
||||
ariaLabel?: string,
|
||||
ariaLabel?: string;
|
||||
|
||||
/**
|
||||
* The decorated component (ToolboxButton).
|
||||
*/
|
||||
children: React$Node,
|
||||
children: React.ReactNode;
|
||||
|
||||
/**
|
||||
* Icon of the button.
|
||||
*/
|
||||
icon: Function,
|
||||
icon: Function;
|
||||
|
||||
/**
|
||||
* Flag used for disabling the small icon.
|
||||
*/
|
||||
iconDisabled: boolean,
|
||||
iconDisabled: boolean;
|
||||
|
||||
/**
|
||||
* The ID of the icon button.
|
||||
*/
|
||||
iconId: string,
|
||||
iconId: string;
|
||||
|
||||
/**
|
||||
* Popover close callback.
|
||||
*/
|
||||
onPopoverClose: Function,
|
||||
onPopoverClose: Function;
|
||||
|
||||
/**
|
||||
* Popover open callback.
|
||||
*/
|
||||
onPopoverOpen: Function,
|
||||
onPopoverOpen: Function;
|
||||
|
||||
/**
|
||||
* The content that will be displayed inside the popover.
|
||||
*/
|
||||
popoverContent: React$Node,
|
||||
popoverContent: React.ReactNode;
|
||||
|
||||
/**
|
||||
* Additional styles.
|
||||
*/
|
||||
styles?: Object,
|
||||
styles?: Object;
|
||||
|
||||
/**
|
||||
* Whether or not the popover is visible.
|
||||
*/
|
||||
visible: boolean
|
||||
};
|
||||
|
||||
declare var APP: Object;
|
||||
visible: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the `ToolboxButtonWithIcon` component.
|
||||
@@ -81,7 +77,7 @@ declare var APP: Object;
|
||||
* @param {Object} props - Component's props.
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
export default function ToolboxButtonWithIconPopup(props: Props) {
|
||||
export default function ToolboxButtonWithIconPopup(props: IProps) {
|
||||
const {
|
||||
ariaControls,
|
||||
ariaExpanded,
|
||||
@@ -98,7 +94,7 @@ export default function ToolboxButtonWithIconPopup(props: Props) {
|
||||
visible
|
||||
} = props;
|
||||
|
||||
const iconProps = {};
|
||||
const iconProps: any = {};
|
||||
|
||||
if (iconDisabled) {
|
||||
iconProps.className
|
||||
@@ -116,7 +112,7 @@ export default function ToolboxButtonWithIconPopup(props: Props) {
|
||||
return (
|
||||
<div
|
||||
className = 'settings-button-container'
|
||||
styles = { styles }>
|
||||
style = { styles }>
|
||||
{children}
|
||||
<div className = 'settings-button-small-icon-container'>
|
||||
<Popover
|
||||
@@ -44,6 +44,7 @@ export interface ITrackOptions {
|
||||
* any.
|
||||
*/
|
||||
export interface ITrack {
|
||||
getOriginalStream: Function;
|
||||
isReceivingData: boolean;
|
||||
jitsiTrack: any;
|
||||
local: boolean;
|
||||
|
||||
@@ -83,7 +83,7 @@ interface IProps {
|
||||
/**
|
||||
* Target elements against which positioning calculations are made.
|
||||
*/
|
||||
offsetTarget?: HTMLElement;
|
||||
offsetTarget?: HTMLElement | null;
|
||||
|
||||
/**
|
||||
* Callback for click on an item in the menu.
|
||||
|
||||
@@ -3,12 +3,12 @@ import { useCallback, useRef, useState } from 'react';
|
||||
import { findAncestorByClass } from '../functions.web';
|
||||
|
||||
|
||||
type RaiseContext = {
|
||||
type RaiseContext<T> = {
|
||||
|
||||
/**
|
||||
* The entity for which the menu is context menu is raised.
|
||||
*/
|
||||
entity?: string | Object;
|
||||
entity?: T;
|
||||
|
||||
/**
|
||||
* Target elements against which positioning calculations are made.
|
||||
@@ -18,13 +18,13 @@ type RaiseContext = {
|
||||
|
||||
const initialState = Object.freeze({});
|
||||
|
||||
const useContextMenu = (): [(force?: boolean | Object) => void,
|
||||
(entity: string | Object, target: HTMLElement | null) => void,
|
||||
(entity: string | Object) => (e: MouseEvent) => void,
|
||||
const useContextMenu = <T>(): [(force?: boolean | Object) => void,
|
||||
(entity: T, target: HTMLElement | null) => void,
|
||||
(entity: T) => (e?: MouseEvent) => void,
|
||||
() => void,
|
||||
() => void,
|
||||
RaiseContext] => {
|
||||
const [ raiseContext, setRaiseContext ] = useState < RaiseContext >(initialState);
|
||||
RaiseContext<T>] => {
|
||||
const [ raiseContext, setRaiseContext ] = useState < RaiseContext<T> >(initialState);
|
||||
const isMouseOverMenu = useRef(false);
|
||||
|
||||
const lowerMenu = useCallback((force: boolean | Object = false) => {
|
||||
@@ -45,21 +45,21 @@ const useContextMenu = (): [(force?: boolean | Object) => void,
|
||||
});
|
||||
}, [ raiseContext ]);
|
||||
|
||||
const raiseMenu = useCallback((entity: string | Object, target: HTMLElement | null) => {
|
||||
const raiseMenu = useCallback((entity: T, target: HTMLElement | null) => {
|
||||
setRaiseContext({
|
||||
entity,
|
||||
offsetTarget: findAncestorByClass(target, 'list-item-container')
|
||||
});
|
||||
}, [ raiseContext ]);
|
||||
|
||||
const toggleMenu = useCallback((entity: string | Object) => (e: MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
const toggleMenu = useCallback((entity: T) => (e?: MouseEvent) => {
|
||||
e?.stopPropagation();
|
||||
const { entity: raisedEntity } = raiseContext;
|
||||
|
||||
if (raisedEntity && raisedEntity === entity) {
|
||||
lowerMenu();
|
||||
} else {
|
||||
raiseMenu(entity, e.target as HTMLElement);
|
||||
raiseMenu(entity, e?.target as HTMLElement);
|
||||
}
|
||||
}, [ raiseContext ]);
|
||||
|
||||
|
||||
@@ -1,38 +1,31 @@
|
||||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { WithTranslation } from 'react-i18next';
|
||||
|
||||
import { translate } from '../../base/i18n';
|
||||
|
||||
import { translate } from '../../base/i18n/functions';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of
|
||||
* {@link MicrosoftSignInButton}.
|
||||
*/
|
||||
type Props = {
|
||||
interface IProps extends WithTranslation {
|
||||
|
||||
/**
|
||||
* The callback to invoke when {@code MicrosoftSignInButton} is clicked.
|
||||
*/
|
||||
onClick: Function,
|
||||
onClick: (e?: React.MouseEvent) => void;
|
||||
|
||||
/**
|
||||
* The text to display within {@code MicrosoftSignInButton}.
|
||||
*/
|
||||
text: string,
|
||||
|
||||
/**
|
||||
* Invoked to obtain translated strings.
|
||||
*/
|
||||
t: Function
|
||||
};
|
||||
text: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A React Component showing a button to sign in with Microsoft.
|
||||
*
|
||||
* @augments Component
|
||||
*/
|
||||
class MicrosoftSignInButton extends Component<Props> {
|
||||
class MicrosoftSignInButton extends Component<IProps> {
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
@@ -5,7 +5,7 @@ import { useDispatch, useSelector } from 'react-redux';
|
||||
import ReactionButton from '../../../reactions/components/web/ReactionButton';
|
||||
import { showOverflowDrawer } from '../../../toolbox/functions.web';
|
||||
import { setGifDrawerVisibility, setGifMenuVisibility } from '../../actions';
|
||||
import { isGifsMenuOpen } from '../../functions';
|
||||
import { isGifsMenuOpen } from '../../functions.web';
|
||||
|
||||
const GifsMenuButton = () => {
|
||||
const menuOpen = useSelector(isGifsMenuOpen);
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component } from 'react';
|
||||
import React, { Component } from 'react';
|
||||
import { WithTranslation } from 'react-i18next';
|
||||
|
||||
/**
|
||||
@@ -9,13 +9,18 @@ interface IProps extends WithTranslation {
|
||||
/**
|
||||
* The callback to invoke when the button is clicked.
|
||||
*/
|
||||
onClick: Function;
|
||||
onClick: (e?: React.MouseEvent) => void;
|
||||
|
||||
/**
|
||||
* True if the user is signed in, so it needs to render a different label
|
||||
* and maybe different style (for the future).
|
||||
*/
|
||||
signedIn?: boolean;
|
||||
|
||||
/**
|
||||
* The text to display within {@code GoogleSignInButton}.
|
||||
*/
|
||||
text?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { translate } from '../../base/i18n';
|
||||
import { translate } from '../../base/i18n/functions';
|
||||
|
||||
import AbstractGoogleSignInButton from './AbstractGoogleSignInButton';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { ReactElement, useCallback, useState } from 'react';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
@@ -9,6 +9,7 @@ import Icon from '../../../../../base/icons/components/Icon';
|
||||
import { IconArrowDown, IconArrowUp } from '../../../../../base/icons/svg';
|
||||
import { isLocalParticipantModerator } from '../../../../../base/participants/functions';
|
||||
import { withPixelLineHeight } from '../../../../../base/styles/functions.web';
|
||||
import { IRoom } from '../../../../../breakout-rooms/types';
|
||||
import { showOverflowDrawer } from '../../../../../toolbox/functions.web';
|
||||
import { ACTION_TRIGGER } from '../../../../constants';
|
||||
import { participantMatchesSearch } from '../../../../functions';
|
||||
@@ -25,7 +26,7 @@ interface IProps {
|
||||
/**
|
||||
* React children.
|
||||
*/
|
||||
children: ReactElement;
|
||||
children: React.ReactNode;
|
||||
|
||||
/**
|
||||
* Is this item highlighted/raised.
|
||||
@@ -47,6 +48,8 @@ interface IProps {
|
||||
*/
|
||||
participantContextEntity?: {
|
||||
jid: string;
|
||||
participantName: string;
|
||||
room: IRoom;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,47 +1,44 @@
|
||||
// @flow
|
||||
|
||||
import React, { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { createBreakoutRoomsEvent, sendAnalytics } from '../../../../../analytics';
|
||||
import {
|
||||
IconCloseLarge,
|
||||
IconRingGroup
|
||||
} from '../../../../../base/icons';
|
||||
import { isLocalParticipantModerator } from '../../../../../base/participants';
|
||||
import { createBreakoutRoomsEvent } from '../../../../../analytics/AnalyticsEvents';
|
||||
import { sendAnalytics } from '../../../../../analytics/functions';
|
||||
import { IconCloseLarge, IconRingGroup } from '../../../../../base/icons/svg';
|
||||
import { isLocalParticipantModerator } from '../../../../../base/participants/functions';
|
||||
import ContextMenu from '../../../../../base/ui/components/web/ContextMenu';
|
||||
import ContextMenuItemGroup from '../../../../../base/ui/components/web/ContextMenuItemGroup';
|
||||
import { closeBreakoutRoom, moveToRoom, removeBreakoutRoom } from '../../../../../breakout-rooms/actions';
|
||||
import { showOverflowDrawer } from '../../../../../toolbox/functions';
|
||||
import { IRoom } from '../../../../../breakout-rooms/types';
|
||||
import { showOverflowDrawer } from '../../../../../toolbox/functions.web';
|
||||
|
||||
type Props = {
|
||||
interface IProps {
|
||||
|
||||
/**
|
||||
* Room reference.
|
||||
*/
|
||||
entity: Object,
|
||||
entity?: IRoom;
|
||||
|
||||
/**
|
||||
* Target elements against which positioning calculations are made.
|
||||
*/
|
||||
offsetTarget: ?HTMLElement,
|
||||
offsetTarget?: HTMLElement | null;
|
||||
|
||||
/**
|
||||
* Callback for the mouse entering the component.
|
||||
*/
|
||||
onEnter: Function,
|
||||
onEnter: (e?: React.MouseEvent) => void;
|
||||
|
||||
/**
|
||||
* Callback for the mouse leaving the component.
|
||||
*/
|
||||
onLeave: Function,
|
||||
onLeave: (e?: React.MouseEvent) => void;
|
||||
|
||||
/**
|
||||
* Callback for making a selection in the menu.
|
||||
*/
|
||||
onSelect: Function
|
||||
};
|
||||
onSelect: (e?: React.MouseEvent | boolean) => void;
|
||||
}
|
||||
|
||||
export const RoomContextMenu = ({
|
||||
entity: room,
|
||||
@@ -49,7 +46,7 @@ export const RoomContextMenu = ({
|
||||
onEnter,
|
||||
onLeave,
|
||||
onSelect
|
||||
}: Props) => {
|
||||
}: IProps) => {
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
const isLocalModerator = useSelector(isLocalParticipantModerator);
|
||||
@@ -57,15 +54,15 @@ export const RoomContextMenu = ({
|
||||
|
||||
const onJoinRoom = useCallback(() => {
|
||||
sendAnalytics(createBreakoutRoomsEvent('join'));
|
||||
dispatch(moveToRoom(room.jid));
|
||||
dispatch(moveToRoom(room?.jid));
|
||||
}, [ dispatch, room ]);
|
||||
|
||||
const onRemoveBreakoutRoom = useCallback(() => {
|
||||
dispatch(removeBreakoutRoom(room.jid));
|
||||
dispatch(removeBreakoutRoom(room?.jid ?? ''));
|
||||
}, [ dispatch, room ]);
|
||||
|
||||
const onCloseBreakoutRoom = useCallback(() => {
|
||||
dispatch(closeBreakoutRoom(room.id));
|
||||
dispatch(closeBreakoutRoom(room?.id ?? ''));
|
||||
}, [ dispatch, room ]);
|
||||
|
||||
const isRoomEmpty = !(room?.participants && Object.keys(room.participants).length > 0);
|
||||
@@ -86,17 +83,18 @@ export const RoomContextMenu = ({
|
||||
} : null
|
||||
].filter(Boolean);
|
||||
|
||||
const lowerMenu = useCallback(() => onSelect(true));
|
||||
const lowerMenu = useCallback(() => onSelect(true), []);
|
||||
|
||||
return (
|
||||
<ContextMenu
|
||||
entity = { room }
|
||||
isDrawerOpen = { room }
|
||||
isDrawerOpen = { Boolean(room) }
|
||||
offsetTarget = { offsetTarget }
|
||||
onClick = { lowerMenu }
|
||||
onDrawerClose = { onSelect }
|
||||
onMouseEnter = { onEnter }
|
||||
onMouseLeave = { onLeave }>
|
||||
{/* @ts-ignore */}
|
||||
<ContextMenuItemGroup actions = { actions } />
|
||||
</ContextMenu>
|
||||
);
|
||||
@@ -1,11 +1,9 @@
|
||||
// @flow
|
||||
|
||||
import React, { useCallback } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { isMobileBrowser } from '../../../../../base/environment/utils';
|
||||
import { isLocalParticipantModerator } from '../../../../../base/participants';
|
||||
import { equals } from '../../../../../base/redux';
|
||||
import { isLocalParticipantModerator } from '../../../../../base/participants/functions';
|
||||
import { equals } from '../../../../../base/redux/functions';
|
||||
import useContextMenu from '../../../../../base/ui/hooks/useContextMenu.web';
|
||||
import {
|
||||
getBreakoutRooms,
|
||||
@@ -14,7 +12,8 @@ import {
|
||||
isAutoAssignParticipantsVisible,
|
||||
isInBreakoutRoom
|
||||
} from '../../../../../breakout-rooms/functions';
|
||||
import { showOverflowDrawer } from '../../../../../toolbox/functions';
|
||||
import { IRoom } from '../../../../../breakout-rooms/types';
|
||||
import { showOverflowDrawer } from '../../../../../toolbox/functions.web';
|
||||
|
||||
import { AutoAssignButton } from './AutoAssignButton';
|
||||
import { CollapsibleRoom } from './CollapsibleRoom';
|
||||
@@ -24,36 +23,40 @@ import RoomActionEllipsis from './RoomActionEllipsis';
|
||||
import { RoomContextMenu } from './RoomContextMenu';
|
||||
import { RoomParticipantContextMenu } from './RoomParticipantContextMenu';
|
||||
|
||||
type Props = {
|
||||
interface IProps {
|
||||
|
||||
/**
|
||||
* Participants search string.
|
||||
*/
|
||||
searchString: string
|
||||
searchString: string;
|
||||
}
|
||||
|
||||
export const RoomList = ({ searchString }: Props) => {
|
||||
export const RoomList = ({ searchString }: IProps) => {
|
||||
const currentRoomId = useSelector(getCurrentRoomId);
|
||||
const rooms = Object.values(useSelector(getBreakoutRooms, equals))
|
||||
.filter((room: Object) => room.id !== currentRoomId)
|
||||
.sort((p1: Object, p2: Object) => (p1?.name || '').localeCompare(p2?.name || ''));
|
||||
.filter((room: IRoom) => room.id !== currentRoomId)
|
||||
.sort((p1?: IRoom, p2?: IRoom) => (p1?.name || '').localeCompare(p2?.name || ''));
|
||||
const inBreakoutRoom = useSelector(isInBreakoutRoom);
|
||||
const isLocalModerator = useSelector(isLocalParticipantModerator);
|
||||
const showAutoAssign = useSelector(isAutoAssignParticipantsVisible);
|
||||
const { hideJoinRoomButton } = useSelector(getBreakoutRoomsConfig);
|
||||
const overflowDrawer = useSelector(showOverflowDrawer);
|
||||
const [ lowerMenu, raiseMenu, toggleMenu, menuEnter, menuLeave, raiseContext ] = useContextMenu();
|
||||
const [ lowerMenu, raiseMenu, toggleMenu, menuEnter, menuLeave, raiseContext ] = useContextMenu<IRoom>();
|
||||
const [ lowerParticipantMenu, raiseParticipantMenu, toggleParticipantMenu,
|
||||
participantMenuEnter, participantMenuLeave, raiseParticipantContext ] = useContextMenu();
|
||||
participantMenuEnter, participantMenuLeave, raiseParticipantContext ] = useContextMenu<{
|
||||
jid: string;
|
||||
participantName: string;
|
||||
room: IRoom;
|
||||
}>();
|
||||
const hideMenu = useCallback(() => !overflowDrawer && lowerMenu(), [ overflowDrawer, lowerMenu ]);
|
||||
const onRaiseMenu = useCallback(room => target => raiseMenu(room, target), [ raiseMenu ]);
|
||||
const onRaiseMenu = useCallback(room => (target: HTMLElement) => raiseMenu(room, target), [ raiseMenu ]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{inBreakoutRoom && <LeaveButton />}
|
||||
{showAutoAssign && <AutoAssignButton />}
|
||||
<div id = 'breakout-rooms-list'>
|
||||
{rooms.map((room: Object) => (
|
||||
{rooms.map(room => (
|
||||
<React.Fragment key = { room.id }>
|
||||
<CollapsibleRoom
|
||||
isHighlighted = { raiseContext.entity === room }
|
||||
@@ -9,8 +9,6 @@ import ContextMenu from '../../../../../base/ui/components/web/ContextMenu';
|
||||
import ContextMenuItemGroup from '../../../../../base/ui/components/web/ContextMenuItemGroup';
|
||||
import { getBreakoutRooms } from '../../../../../breakout-rooms/functions';
|
||||
import { showOverflowDrawer } from '../../../../../toolbox/functions.web';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import SendToRoomButton from '../../../../../video-menu/components/web/SendToRoomButton';
|
||||
import { AVATAR_SIZE } from '../../../../constants';
|
||||
|
||||
@@ -20,7 +18,7 @@ interface IProps {
|
||||
/**
|
||||
* Room and participant jid reference.
|
||||
*/
|
||||
entity: {
|
||||
entity?: {
|
||||
jid: string;
|
||||
participantName: string;
|
||||
room: any;
|
||||
@@ -29,7 +27,7 @@ interface IProps {
|
||||
/**
|
||||
* Target elements against which positioning calculations are made.
|
||||
*/
|
||||
offsetTarget: HTMLElement | undefined;
|
||||
offsetTarget?: HTMLElement | null;
|
||||
|
||||
/**
|
||||
* Callback for the mouse entering the component.
|
||||
@@ -80,15 +78,15 @@ export const RoomParticipantContextMenu = ({
|
||||
return (<SendToRoomButton
|
||||
key = { room.id }
|
||||
onClick = { lowerMenu }
|
||||
participantID = { entity?.jid }
|
||||
participantID = { entity?.jid ?? '' }
|
||||
room = { room } />);
|
||||
}
|
||||
|
||||
return null;
|
||||
})
|
||||
.filter(Boolean), [ entity, rooms ]);
|
||||
.filter(Boolean), [ entity, rooms ]);
|
||||
|
||||
return isLocalModerator && (
|
||||
return isLocalModerator ? (
|
||||
<ContextMenu
|
||||
entity = { entity }
|
||||
isDrawerOpen = { Boolean(entity) }
|
||||
@@ -112,5 +110,5 @@ export const RoomParticipantContextMenu = ({
|
||||
{breakoutRoomsButtons}
|
||||
</ContextMenuItemGroup>
|
||||
</ContextMenu>
|
||||
);
|
||||
) : null;
|
||||
};
|
||||
|
||||
@@ -83,7 +83,7 @@ function MeetingParticipants({
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [ lowerMenu, , toggleMenu, menuEnter, menuLeave, raiseContext ] = useContextMenu();
|
||||
const [ lowerMenu, , toggleMenu, menuEnter, menuLeave, raiseContext ] = useContextMenu<string>();
|
||||
const muteAudio = useCallback(id => () => {
|
||||
dispatch(muteRemote(id, MEDIA_TYPE.AUDIO));
|
||||
dispatch(rejectParticipantAudio(id));
|
||||
|
||||
@@ -21,8 +21,6 @@ import {
|
||||
isMuteAllVisible
|
||||
} from '../../functions';
|
||||
import { AddBreakoutRoomButton } from '../breakout-rooms/components/web/AddBreakoutRoomButton';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import { RoomList } from '../breakout-rooms/components/web/RoomList';
|
||||
|
||||
import { FooterContextMenu } from './FooterContextMenu';
|
||||
|
||||
@@ -1,36 +1,34 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
|
||||
type Props = {
|
||||
interface IProps {
|
||||
|
||||
/**
|
||||
* The text for the Label.
|
||||
*/
|
||||
children: React$Node,
|
||||
children: React.ReactElement;
|
||||
|
||||
/**
|
||||
* The CSS class of the label.
|
||||
*/
|
||||
className?: string,
|
||||
className?: string;
|
||||
|
||||
/**
|
||||
* The (round) number prefix for the Label.
|
||||
*/
|
||||
number?: string | number,
|
||||
number?: string | number;
|
||||
|
||||
/**
|
||||
* The click handler.
|
||||
*/
|
||||
onClick?: Function,
|
||||
};
|
||||
onClick?: (e?: React.MouseEvent) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Label for the dialogs.
|
||||
*
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
function Label({ children, className, number, onClick }: Props) {
|
||||
function Label({ children, className, number, onClick }: IProps) {
|
||||
const containerClass = className
|
||||
? `prejoin-dialog-label ${className}`
|
||||
: 'prejoin-dialog-label';
|
||||
@@ -6,8 +6,6 @@ import Avatar from '../../../../base/avatar/components/Avatar';
|
||||
import { translate } from '../../../../base/i18n/functions';
|
||||
import Icon from '../../../../base/icons/components/Icon';
|
||||
import { IconCloseLarge } from '../../../../base/icons/svg';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import Label from '../Label';
|
||||
|
||||
interface IProps extends WithTranslation {
|
||||
|
||||
@@ -7,8 +7,6 @@ import Icon from '../../../../base/icons/components/Icon';
|
||||
import { IconArrowLeft } from '../../../../base/icons/svg';
|
||||
import Button from '../../../../base/ui/components/web/Button';
|
||||
import { getCountryCodeFromPhone } from '../../../utils';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import Label from '../Label';
|
||||
|
||||
interface IProps extends WithTranslation {
|
||||
|
||||
@@ -6,8 +6,6 @@ import { translate } from '../../../../base/i18n/functions';
|
||||
import Icon from '../../../../base/icons/components/Icon';
|
||||
import { IconCloseLarge } from '../../../../base/icons/svg';
|
||||
import Button from '../../../../base/ui/components/web/Button';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import Label from '../Label';
|
||||
import CountryPicker from '../country-picker/CountryPicker';
|
||||
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { IconRaiseHand } from '../../../base/icons';
|
||||
import { getLocalParticipant, hasRaisedHand } from '../../../base/participants';
|
||||
import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components';
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
import { IconRaiseHand } from '../../../base/icons/svg';
|
||||
import { getLocalParticipant, hasRaisedHand } from '../../../base/participants/functions';
|
||||
import AbstractButton, { IProps as AbstractButtonProps } from '../../../base/toolbox/components/AbstractButton';
|
||||
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link RaiseHandButton}.
|
||||
*/
|
||||
type Props = AbstractButtonProps & {
|
||||
interface IProps extends AbstractButtonProps {
|
||||
|
||||
/**
|
||||
* Whether or not the hand is raised.
|
||||
*/
|
||||
raisedHand: boolean,
|
||||
};
|
||||
raisedHand: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of a button for raising hand.
|
||||
*/
|
||||
class RaiseHandButton extends AbstractButton<Props, *> {
|
||||
class RaiseHandButton extends AbstractButton<IProps> {
|
||||
accessibilityLabel = 'toolbar.accessibilityLabel.raiseHand';
|
||||
toggledAccessibilityLabel = 'toolbar.accessibilityLabel.lowerHand';
|
||||
icon = IconRaiseHand;
|
||||
@@ -48,7 +49,7 @@ class RaiseHandButton extends AbstractButton<Props, *> {
|
||||
* @param {Object} state - Redux state.
|
||||
* @returns {Object}
|
||||
*/
|
||||
const mapStateToProps = state => {
|
||||
const mapStateToProps = (state: IReduxState) => {
|
||||
const localParticipant = getLocalParticipant(state);
|
||||
|
||||
return {
|
||||
@@ -1,47 +1,46 @@
|
||||
/* @flow */
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import Tooltip from '../../../base/tooltip/components/Tooltip';
|
||||
import AbstractToolbarButton from '../../../toolbox/components/AbstractToolbarButton';
|
||||
import type { Props as AbstractToolbarButtonProps } from '../../../toolbox/components/AbstractToolbarButton';
|
||||
import AbstractToolbarButton, {
|
||||
IProps as AbstractToolbarButtonProps
|
||||
} from '../../../toolbox/components/AbstractToolbarButton';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link ReactionButton}.
|
||||
*/
|
||||
type Props = AbstractToolbarButtonProps & {
|
||||
interface IProps extends AbstractToolbarButtonProps {
|
||||
|
||||
/**
|
||||
* Optional label for the button.
|
||||
*/
|
||||
label?: string;
|
||||
|
||||
/**
|
||||
* Optional text to display in the tooltip.
|
||||
*/
|
||||
tooltip?: string,
|
||||
tooltip?: string;
|
||||
|
||||
/**
|
||||
* From which direction the tooltip should appear, relative to the
|
||||
* button.
|
||||
*/
|
||||
tooltipPosition: string,
|
||||
|
||||
/**
|
||||
* Optional label for the button.
|
||||
*/
|
||||
label?: string
|
||||
};
|
||||
tooltipPosition: 'top' | 'bottom' | 'left' | 'right';
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} state of {@link ReactionButton}.
|
||||
*/
|
||||
type State = {
|
||||
interface IState {
|
||||
|
||||
/**
|
||||
* Used to determine zoom level on reaction burst.
|
||||
*/
|
||||
increaseLevel: number,
|
||||
increaseLevel: number;
|
||||
|
||||
/**
|
||||
* Timeout ID to reset reaction burst.
|
||||
*/
|
||||
increaseTimeout: TimeoutID | null
|
||||
increaseTimeout: number | null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,7 +48,7 @@ type State = {
|
||||
*
|
||||
* @augments AbstractToolbarButton
|
||||
*/
|
||||
class ReactionButton extends AbstractToolbarButton<Props, State> {
|
||||
class ReactionButton extends AbstractToolbarButton<IProps, IState> {
|
||||
/**
|
||||
* Default values for {@code ReactionButton} component's properties.
|
||||
*
|
||||
@@ -64,7 +63,7 @@ class ReactionButton extends AbstractToolbarButton<Props, State> {
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this._onKeyDown = this._onKeyDown.bind(this);
|
||||
@@ -76,10 +75,6 @@ class ReactionButton extends AbstractToolbarButton<Props, State> {
|
||||
};
|
||||
}
|
||||
|
||||
_onKeyDown: (Object) => void;
|
||||
|
||||
_onClickHandler: () => void;
|
||||
|
||||
/**
|
||||
* Handles 'Enter' key on the button to trigger onClick for accessibility.
|
||||
* We should be handling Space onKeyUp but it conflicts with PTT.
|
||||
@@ -88,7 +83,7 @@ class ReactionButton extends AbstractToolbarButton<Props, State> {
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onKeyDown(event) {
|
||||
_onKeyDown(event: React.KeyboardEvent) {
|
||||
// If the event coming to the dialog has been subject to preventDefault
|
||||
// we don't handle it here.
|
||||
if (event.defaultPrevented) {
|
||||
@@ -109,8 +104,8 @@ class ReactionButton extends AbstractToolbarButton<Props, State> {
|
||||
*/
|
||||
_onClickHandler() {
|
||||
this.props.onClick();
|
||||
clearTimeout(this.state.increaseTimeout);
|
||||
const timeout = setTimeout(() => {
|
||||
clearTimeout(this.state.increaseTimeout ?? 0);
|
||||
const timeout = window.setTimeout(() => {
|
||||
this.setState({
|
||||
increaseLevel: 0
|
||||
});
|
||||
@@ -132,7 +127,7 @@ class ReactionButton extends AbstractToolbarButton<Props, State> {
|
||||
* @protected
|
||||
* @returns {ReactElement} The button of this {@code ReactionButton}.
|
||||
*/
|
||||
_renderButton(children) {
|
||||
_renderButton(children: React.ReactElement) {
|
||||
return (
|
||||
<div
|
||||
aria-label = { this.props.accessibilityLabel }
|
||||
@@ -10,8 +10,6 @@ import { isMobileBrowser } from '../../../base/environment/utils';
|
||||
import { raiseHand } from '../../../base/participants/actions';
|
||||
import { getLocalParticipant, hasRaisedHand } from '../../../base/participants/functions';
|
||||
import GifsMenu from '../../../gifs/components/web/GifsMenu';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import GifsMenuButton from '../../../gifs/components/web/GifsMenuButton';
|
||||
import { isGifEnabled, isGifsMenuOpen } from '../../../gifs/functions';
|
||||
import { dockToolbox } from '../../../toolbox/actions.web';
|
||||
@@ -19,7 +17,6 @@ import { addReactionToBuffer } from '../../actions.any';
|
||||
import { toggleReactionsMenuVisibility } from '../../actions.web';
|
||||
import { REACTIONS, REACTIONS_MENU_HEIGHT } from '../../constants';
|
||||
|
||||
// @ts-ignore
|
||||
import ReactionButton from './ReactionButton';
|
||||
|
||||
interface IProps {
|
||||
|
||||
@@ -6,15 +6,12 @@ import { IReduxState } from '../../../app/types';
|
||||
import { isMobileBrowser } from '../../../base/environment/utils';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
import { IconArrowUp } from '../../../base/icons/svg';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import ToolboxButtonWithIconPopup from '../../../base/toolbox/components/web/ToolboxButtonWithIconPopup';
|
||||
import { toggleReactionsMenuVisibility } from '../../actions.web';
|
||||
import { IReactionEmojiProps } from '../../constants';
|
||||
import { getReactionsQueue, isReactionsEnabled } from '../../functions.any';
|
||||
import { getReactionsMenuVisibility } from '../../functions.web';
|
||||
|
||||
// @ts-ignore
|
||||
import RaiseHandButton from './RaiseHandButton';
|
||||
import ReactionEmoji from './ReactionEmoji';
|
||||
import ReactionsMenu from './ReactionsMenu';
|
||||
|
||||
@@ -4,8 +4,7 @@ import JitsiMeetJS from '../base/lib-jitsi-meet';
|
||||
import { showNotification } from '../notifications/actions';
|
||||
import { NOTIFICATION_TIMEOUT_TYPE } from '../notifications/constants';
|
||||
|
||||
// @ts-ignore
|
||||
import { RecordingLimitNotificationDescription } from './components';
|
||||
import RecordingLimitNotificationDescription from './components/web/RecordingLimitNotificationDescription';
|
||||
|
||||
export * from './actions.any';
|
||||
|
||||
|
||||
@@ -196,7 +196,7 @@ export function _mapStateToProps(state: IReduxState, ownProps: IProps) {
|
||||
const { mode } = ownProps;
|
||||
|
||||
return {
|
||||
_iAmRecorder: state['features/base/config'].iAmRecorder,
|
||||
_iAmRecorder: Boolean(state['features/base/config'].iAmRecorder),
|
||||
_status: getSessionStatusToShow(state, mode)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ export interface IProps {
|
||||
/**
|
||||
* The {@code JitsiConference} for the current conference.
|
||||
*/
|
||||
_conference: IJitsiConference;
|
||||
_conference?: IJitsiConference;
|
||||
|
||||
/**
|
||||
* The current state of interactions with the Google API. Determines what
|
||||
@@ -32,7 +32,7 @@ export interface IProps {
|
||||
/**
|
||||
* The live stream key that was used before.
|
||||
*/
|
||||
_streamKey: string;
|
||||
_streamKey?: string;
|
||||
|
||||
/**
|
||||
* The Redux dispatch function.
|
||||
@@ -72,7 +72,7 @@ export interface IState {
|
||||
/**
|
||||
* The selected or entered stream key to use for YouTube live streaming.
|
||||
*/
|
||||
streamKey: string;
|
||||
streamKey?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -159,7 +159,9 @@ export default class AbstractStartLiveStreamDialog<P extends IProps>
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_onGetYouTubeBroadcasts: () => Promise<any>;
|
||||
_onGetYouTubeBroadcasts(): Promise<any> | void {
|
||||
// to be overwritten by child classes.
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback invoked to update the {@code StartLiveStreamDialog} component's
|
||||
@@ -205,7 +207,7 @@ export default class AbstractStartLiveStreamDialog<P extends IProps>
|
||||
sendAnalytics(
|
||||
createLiveStreamingDialogEvent('start', 'confirm.button'));
|
||||
|
||||
this.props._conference.startRecording({
|
||||
this.props._conference?.startRecording({
|
||||
broadcastId: selectedBroadcastID,
|
||||
mode: JitsiRecordingConstants.mode.STREAM,
|
||||
streamId: key
|
||||
|
||||
@@ -7,6 +7,7 @@ import { IReduxState } from '../../../app/types';
|
||||
import { IJitsiConference } from '../../../base/conference/reducer';
|
||||
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
|
||||
import { getActiveSession } from '../../functions';
|
||||
import { ISessionData } from '../../reducer';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of
|
||||
@@ -17,12 +18,12 @@ interface IProps extends WithTranslation {
|
||||
/**
|
||||
* The {@code JitsiConference} for the current conference.
|
||||
*/
|
||||
_conference: IJitsiConference;
|
||||
_conference?: IJitsiConference;
|
||||
|
||||
/**
|
||||
* The redux representation of the live streaming to be stopped.
|
||||
*/
|
||||
_session: { id: string; };
|
||||
_session?: ISessionData;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,7 +58,7 @@ export default class AbstractStopLiveStreamDialog extends Component<IProps> {
|
||||
const { _session } = this.props;
|
||||
|
||||
if (_session) {
|
||||
this.props._conference.stopRecording(_session.id);
|
||||
this.props._conference?.stopRecording(_session.id);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -9,12 +9,8 @@ import AbstractLiveStreamButton, {
|
||||
_mapStateToProps as _abstractMapStateToProps
|
||||
} from '../AbstractLiveStreamButton';
|
||||
|
||||
import {
|
||||
StartLiveStreamDialog,
|
||||
StopLiveStreamDialog
|
||||
|
||||
// @ts-ignore
|
||||
} from './index';
|
||||
import StartLiveStreamDialog from './StartLiveStreamDialog';
|
||||
import StopLiveStreamDialog from './StopLiveStreamDialog';
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { translate } from '../../../../base/i18n';
|
||||
import { IReduxState } from '../../../../app/types';
|
||||
import { translate } from '../../../../base/i18n/functions';
|
||||
import Dialog from '../../../../base/ui/components/web/Dialog';
|
||||
import Spinner from '../../../../base/ui/components/web/Spinner';
|
||||
import {
|
||||
@@ -15,22 +14,24 @@ import {
|
||||
showAccountSelection,
|
||||
signIn,
|
||||
updateProfile
|
||||
|
||||
// @ts-ignore
|
||||
} from '../../../../google-api';
|
||||
import AbstractStartLiveStreamDialog, {
|
||||
type Props as AbstractProps,
|
||||
IProps as AbstractProps,
|
||||
_mapStateToProps as _abstractMapStateToProps
|
||||
} from '../AbstractStartLiveStreamDialog';
|
||||
|
||||
import StreamKeyForm from './StreamKeyForm';
|
||||
import StreamKeyPicker from './StreamKeyPicker';
|
||||
|
||||
type Props = AbstractProps & {
|
||||
interface IProps extends AbstractProps {
|
||||
|
||||
/**
|
||||
* The ID for the Google client application used for making stream key
|
||||
* related requests.
|
||||
*/
|
||||
_googleApiApplicationClientID: string
|
||||
_googleApiApplicationClientID?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -40,15 +41,15 @@ type Props = AbstractProps & {
|
||||
* @augments Component
|
||||
*/
|
||||
class StartLiveStreamDialog
|
||||
extends AbstractStartLiveStreamDialog<Props> {
|
||||
extends AbstractStartLiveStreamDialog<IProps> {
|
||||
|
||||
/**
|
||||
* Initializes a new {@code StartLiveStreamDialog} instance.
|
||||
*
|
||||
* @param {Props} props - The React {@code Component} props to initialize
|
||||
* @param {IProps} props - The React {@code Component} props to initialize
|
||||
* the new {@code StartLiveStreamDialog} instance with.
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
// Bind event handlers so they are only bound once per instance.
|
||||
@@ -95,19 +96,13 @@ class StartLiveStreamDialog
|
||||
<StreamKeyForm
|
||||
onChange = { this._onStreamKeyChange }
|
||||
value = {
|
||||
this.state.streamKey || this.props._streamKey
|
||||
this.state.streamKey || this.props._streamKey || ''
|
||||
} />
|
||||
</div>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
_onCancel: () => boolean;
|
||||
|
||||
_onSubmit: () => boolean;
|
||||
|
||||
_onInitializeGoogleApi: () => void;
|
||||
|
||||
/**
|
||||
* Loads the Google web client application used for fetching stream keys.
|
||||
* If the user is already logged in, then a request for available YouTube
|
||||
@@ -118,7 +113,7 @@ class StartLiveStreamDialog
|
||||
*/
|
||||
_onInitializeGoogleApi() {
|
||||
this.props.dispatch(loadGoogleAPI())
|
||||
.catch(response => this._parseErrorFromResponse(response));
|
||||
.catch((response: any) => this._parseErrorFromResponse(response));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,15 +123,13 @@ class StartLiveStreamDialog
|
||||
* @inheritdoc
|
||||
* @returns {void}
|
||||
*/
|
||||
componentDidUpdate(previousProps) {
|
||||
componentDidUpdate(previousProps: IProps) {
|
||||
if (previousProps._googleAPIState === GOOGLE_API_STATES.LOADED
|
||||
&& this.props._googleAPIState === GOOGLE_API_STATES.SIGNED_IN) {
|
||||
this._onGetYouTubeBroadcasts();
|
||||
}
|
||||
}
|
||||
|
||||
_onGetYouTubeBroadcasts: () => void;
|
||||
|
||||
/**
|
||||
* Asks the user to sign in, if not already signed in, and then requests a
|
||||
* list of the user's YouTube broadcasts.
|
||||
@@ -146,10 +139,10 @@ class StartLiveStreamDialog
|
||||
*/
|
||||
_onGetYouTubeBroadcasts() {
|
||||
this.props.dispatch(updateProfile())
|
||||
.catch(response => this._parseErrorFromResponse(response));
|
||||
.catch((response: any) => this._parseErrorFromResponse(response));
|
||||
|
||||
this.props.dispatch(requestAvailableYouTubeBroadcasts())
|
||||
.then(broadcasts => {
|
||||
.then((broadcasts: { boundStreamID: string; }[]) => {
|
||||
this._setStateIfMounted({
|
||||
broadcasts
|
||||
});
|
||||
@@ -160,11 +153,9 @@ class StartLiveStreamDialog
|
||||
this._onYouTubeBroadcastIDSelected(broadcast.boundStreamID);
|
||||
}
|
||||
})
|
||||
.catch(response => this._parseErrorFromResponse(response));
|
||||
.catch((response: any) => this._parseErrorFromResponse(response));
|
||||
}
|
||||
|
||||
_onGoogleSignIn: () => Object;
|
||||
|
||||
/**
|
||||
* Forces the Google web client application to prompt for a sign in, such as
|
||||
* when changing account, and will then fetch available YouTube broadcasts.
|
||||
@@ -174,11 +165,9 @@ class StartLiveStreamDialog
|
||||
*/
|
||||
_onGoogleSignIn() {
|
||||
this.props.dispatch(signIn())
|
||||
.catch(response => this._parseErrorFromResponse(response));
|
||||
.catch((response: any) => this._parseErrorFromResponse(response));
|
||||
}
|
||||
|
||||
_onRequestGoogleSignIn: () => Object;
|
||||
|
||||
/**
|
||||
* Forces the Google web client application to prompt for a sign in, such as
|
||||
* when changing account, and will then fetch available YouTube broadcasts.
|
||||
@@ -198,10 +187,6 @@ class StartLiveStreamDialog
|
||||
.then(() => this._onGetYouTubeBroadcasts());
|
||||
}
|
||||
|
||||
_onStreamKeyChange: string => void;
|
||||
|
||||
_onYouTubeBroadcastIDSelected: (string) => Object;
|
||||
|
||||
/**
|
||||
* Fetches the stream key for a YouTube broadcast and updates the internal
|
||||
* state to display the associated stream key as being entered.
|
||||
@@ -211,10 +196,10 @@ class StartLiveStreamDialog
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_onYouTubeBroadcastIDSelected(boundStreamID) {
|
||||
_onYouTubeBroadcastIDSelected(boundStreamID: string) {
|
||||
this.props.dispatch(
|
||||
requestLiveStreamsForYouTubeBroadcast(boundStreamID))
|
||||
.then(({ streamKey, selectedBoundStreamID }) =>
|
||||
.then(({ streamKey, selectedBoundStreamID }: { selectedBoundStreamID: string; streamKey: string; }) =>
|
||||
this._setStateIfMounted({
|
||||
streamKey,
|
||||
selectedBoundStreamID
|
||||
@@ -232,7 +217,7 @@ class StartLiveStreamDialog
|
||||
* @private
|
||||
* @returns {string|null}
|
||||
*/
|
||||
_parseErrorFromResponse(response) {
|
||||
_parseErrorFromResponse(response: any) {
|
||||
|
||||
if (!response || !response.result) {
|
||||
return;
|
||||
@@ -240,11 +225,11 @@ class StartLiveStreamDialog
|
||||
|
||||
const result = response.result;
|
||||
const error = result.error;
|
||||
const errors = error && error.errors;
|
||||
const firstError = errors && errors[0];
|
||||
const errors = error?.errors;
|
||||
const firstError = errors?.[0];
|
||||
|
||||
this._setStateIfMounted({
|
||||
errorType: (firstError && firstError.reason) || null
|
||||
errorType: firstError?.reason || null
|
||||
});
|
||||
}
|
||||
|
||||
@@ -335,8 +320,6 @@ class StartLiveStreamDialog
|
||||
);
|
||||
}
|
||||
|
||||
_setStateIfMounted: Object => void;
|
||||
|
||||
/**
|
||||
* Returns the error message to display for the current error state.
|
||||
*
|
||||
@@ -369,7 +352,7 @@ class StartLiveStreamDialog
|
||||
* _googleApiApplicationClientID: string
|
||||
* }}
|
||||
*/
|
||||
function _mapStateToProps(state: Object) {
|
||||
function _mapStateToProps(state: IReduxState) {
|
||||
return {
|
||||
..._abstractMapStateToProps(state),
|
||||
_googleApiApplicationClientID:
|
||||
@@ -1,9 +1,7 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { translate } from '../../../../base/i18n';
|
||||
import { translate } from '../../../../base/i18n/functions';
|
||||
import Dialog from '../../../../base/ui/components/web/Dialog';
|
||||
import AbstractStopLiveStreamDialog, {
|
||||
_mapStateToProps
|
||||
@@ -33,8 +31,6 @@ class StopLiveStreamDialog extends AbstractStopLiveStreamDialog {
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
_onSubmit: () => boolean;
|
||||
}
|
||||
|
||||
export default translate(connect(_mapStateToProps)(StopLiveStreamDialog));
|
||||
@@ -1,12 +1,10 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { translate } from '../../../../base/i18n';
|
||||
import { translate } from '../../../../base/i18n/functions';
|
||||
import Input from '../../../../base/ui/components/web/Input';
|
||||
import AbstractStreamKeyForm, {
|
||||
type Props, _mapStateToProps
|
||||
IProps, _mapStateToProps
|
||||
} from '../AbstractStreamKeyForm';
|
||||
|
||||
/**
|
||||
@@ -14,15 +12,15 @@ import AbstractStreamKeyForm, {
|
||||
*
|
||||
* @augments Component
|
||||
*/
|
||||
class StreamKeyForm extends AbstractStreamKeyForm<Props> {
|
||||
class StreamKeyForm extends AbstractStreamKeyForm<IProps> {
|
||||
|
||||
/**
|
||||
* Initializes a new {@code StreamKeyForm} instance.
|
||||
*
|
||||
* @param {Props} props - The React {@code Component} props to initialize
|
||||
* @param {IProps} props - The React {@code Component} props to initialize
|
||||
* the new {@code StreamKeyForm} instance with.
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
// Bind event handlers so they are only bound once per instance.
|
||||
@@ -90,10 +88,6 @@ class StreamKeyForm extends AbstractStreamKeyForm<Props> {
|
||||
);
|
||||
}
|
||||
|
||||
_onInputChange: Object => void;
|
||||
|
||||
_onOpenHelp: () => void;
|
||||
|
||||
/**
|
||||
* Opens a new tab with information on how to manually locate a YouTube
|
||||
* broadcast stream key.
|
||||
@@ -105,8 +99,6 @@ class StreamKeyForm extends AbstractStreamKeyForm<Props> {
|
||||
window.open(this.props._liveStreaming.helpURL, '_blank', 'noopener');
|
||||
}
|
||||
|
||||
_onOpenHelpKeyPress: () => void;
|
||||
|
||||
/**
|
||||
* Opens a new tab with information on how to manually locate a YouTube
|
||||
* broadcast stream key.
|
||||
@@ -116,7 +108,7 @@ class StreamKeyForm extends AbstractStreamKeyForm<Props> {
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onOpenHelpKeyPress(e) {
|
||||
_onOpenHelpKeyPress(e: React.KeyboardEvent) {
|
||||
if (e.key === ' ') {
|
||||
e.preventDefault();
|
||||
this._onOpenHelp();
|
||||
@@ -30,7 +30,7 @@ interface IProps extends WithTranslation {
|
||||
* The boundStreamID of the broadcast that should display as selected in the
|
||||
* dropdown.
|
||||
*/
|
||||
selectedBoundStreamID: string;
|
||||
selectedBoundStreamID?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,7 +103,7 @@ class StreamKeyPicker extends PureComponent<IProps> {
|
||||
label = { t('liveStreaming.choose') }
|
||||
onChange = { this._onSelect }
|
||||
options = { dropdownItems }
|
||||
value = { selectedBoundStreamID } />
|
||||
value = { selectedBoundStreamID ?? '' } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ export function _abstractMapStateToProps(state: IReduxState) {
|
||||
} = getRecordButtonProps(state);
|
||||
|
||||
const canStartRecording = isRecordButtonVisible && !isRecordButtonDisabled;
|
||||
const _visible = (canStartRecording || isRecordingRunning) && Boolean(webhookProxyUrl);
|
||||
const _visible = Boolean((canStartRecording || isRecordingRunning) && Boolean(webhookProxyUrl));
|
||||
|
||||
return {
|
||||
_disabled: !isRecordingRunning,
|
||||
|
||||
@@ -30,7 +30,7 @@ export interface IProps extends WithTranslation {
|
||||
/**
|
||||
* The {@code JitsiConference} for the current conference.
|
||||
*/
|
||||
_conference: IJitsiConference;
|
||||
_conference?: IJitsiConference;
|
||||
|
||||
/**
|
||||
* Whether to show file recordings service, even if integrations
|
||||
@@ -100,12 +100,12 @@ interface IState {
|
||||
/**
|
||||
* Whether the local recording should record just the local user streams.
|
||||
*/
|
||||
localRecordingOnlySelf?: boolean;
|
||||
localRecordingOnlySelf: boolean;
|
||||
|
||||
/**
|
||||
* The currently selected recording service of type: RECORDING_TYPES.
|
||||
*/
|
||||
selectedRecordingService?: string;
|
||||
selectedRecordingService: string;
|
||||
|
||||
/**
|
||||
* True if the user requested the service to share the recording with others.
|
||||
@@ -143,7 +143,7 @@ class AbstractStartRecordingDialog extends Component<IProps, IState> {
|
||||
this._toggleScreenshotCapture = this._toggleScreenshotCapture.bind(this);
|
||||
this._onLocalRecordingSelfChange = this._onLocalRecordingSelfChange.bind(this);
|
||||
|
||||
let selectedRecordingService;
|
||||
let selectedRecordingService = '';
|
||||
|
||||
// TODO: Potentially check if we need to handle changes of
|
||||
// _fileRecordingsServiceEnabled and _areIntegrationsEnabled()
|
||||
@@ -351,7 +351,7 @@ class AbstractStartRecordingDialog extends Component<IProps, IState> {
|
||||
);
|
||||
|
||||
this._toggleScreenshotCapture();
|
||||
_conference.startRecording({
|
||||
_conference?.startRecording({
|
||||
mode: JitsiRecordingConstants.mode.FILE,
|
||||
appData
|
||||
});
|
||||
@@ -408,16 +408,16 @@ export function mapStateToProps(state: IReduxState) {
|
||||
} = state['features/base/config'];
|
||||
|
||||
return {
|
||||
_appKey: dropbox.appKey,
|
||||
_appKey: dropbox.appKey ?? '',
|
||||
_autoCaptionOnRecord: transcription?.autoCaptionOnRecord ?? false,
|
||||
_conference: state['features/base/conference'].conference,
|
||||
_fileRecordingsServiceEnabled: recordingService?.enabled ?? false,
|
||||
_fileRecordingsServiceSharingEnabled: recordingService?.sharingEnabled ?? false,
|
||||
_isDropboxEnabled: isDropboxEnabled(state),
|
||||
_localRecordingEnabled: !localRecording?.disable,
|
||||
_rToken: state['features/dropbox'].rToken,
|
||||
_rToken: state['features/dropbox'].rToken ?? '',
|
||||
_tokenExpireDate: state['features/dropbox'].expireDate,
|
||||
_token: state['features/dropbox'].token
|
||||
_token: state['features/dropbox'].token ?? ''
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -126,12 +126,12 @@ export interface IProps extends WithTranslation {
|
||||
/**
|
||||
* Number of MiB of available space in user's Dropbox account.
|
||||
*/
|
||||
spaceLeft: number | null;
|
||||
spaceLeft?: number;
|
||||
|
||||
/**
|
||||
* The display name of the user's Dropbox account.
|
||||
*/
|
||||
userName: string | null;
|
||||
userName?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -83,8 +83,8 @@ export default class AbstractStopRecordingDialog<P extends IProps>
|
||||
} else {
|
||||
const { _fileRecordingSession } = this.props;
|
||||
|
||||
if (_fileRecordingSession) { // @ts-ignore
|
||||
this.props._conference.stopRecording(_fileRecordingSession.id);
|
||||
if (_fileRecordingSession) {
|
||||
this.props._conference?.stopRecording(_fileRecordingSession.id);
|
||||
this._toggleScreenshotCapture();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,6 @@ import { withStyles } from '@mui/styles';
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
// @ts-ignore
|
||||
import { StartRecordingDialog } from '../..';
|
||||
import { openDialog } from '../../../../base/dialog/actions';
|
||||
import { translate } from '../../../../base/i18n/functions';
|
||||
import { IconHighlight } from '../../../../base/icons/svg';
|
||||
@@ -13,12 +11,13 @@ import Label from '../../../../base/label/components/web/Label';
|
||||
import Tooltip from '../../../../base/tooltip/components/Tooltip';
|
||||
import BaseTheme from '../../../../base/ui/components/BaseTheme.web';
|
||||
import { maybeShowPremiumFeatureDialog } from '../../../../jaas/actions';
|
||||
import StartRecordingDialog from '../../Recording/web/StartRecordingDialog';
|
||||
import AbstractHighlightButton, {
|
||||
IProps as AbstractProps,
|
||||
_abstractMapStateToProps
|
||||
} from '../AbstractHighlightButton';
|
||||
|
||||
type Props = AbstractProps & {
|
||||
interface IProps extends AbstractProps {
|
||||
_disabled: boolean;
|
||||
|
||||
/**
|
||||
@@ -32,7 +31,7 @@ type Props = AbstractProps & {
|
||||
_visible: boolean;
|
||||
|
||||
classes: any;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} state of {@link HighlightButton}.
|
||||
@@ -90,17 +89,16 @@ const styles = (theme: Theme) => {
|
||||
* React {@code Component} responsible for displaying an action that
|
||||
* allows users to highlight a meeting moment.
|
||||
*/
|
||||
export class HighlightButton extends AbstractHighlightButton<Props, IState> {
|
||||
export class HighlightButton extends AbstractHighlightButton<IProps, IState> {
|
||||
/**
|
||||
* Initializes a new HighlightButton instance.
|
||||
*
|
||||
* @param {Object} props - The read-only properties with which the new
|
||||
* instance is to be initialized.
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
// @ts-ignore
|
||||
this.state = {
|
||||
isNotificationOpen: false
|
||||
};
|
||||
@@ -133,7 +131,6 @@ export class HighlightButton extends AbstractHighlightButton<Props, IState> {
|
||||
* @returns {void}
|
||||
*/
|
||||
async _onOpenDialog() {
|
||||
// @ts-ignore
|
||||
const { dispatch } = this.props;
|
||||
const dialogShown = await dispatch(maybeShowPremiumFeatureDialog(MEET_FEATURES.RECORDING));
|
||||
|
||||
@@ -152,11 +149,9 @@ export class HighlightButton extends AbstractHighlightButton<Props, IState> {
|
||||
_onClick(e?: React.MouseEvent) {
|
||||
e?.stopPropagation();
|
||||
|
||||
// @ts-ignore
|
||||
const { _disabled } = this.props;
|
||||
|
||||
if (_disabled) {
|
||||
// @ts-ignore
|
||||
this.setState({
|
||||
isNotificationOpen: true
|
||||
});
|
||||
@@ -171,7 +166,6 @@ export class HighlightButton extends AbstractHighlightButton<Props, IState> {
|
||||
* @returns {void}
|
||||
*/
|
||||
_onWindowClickListener() {
|
||||
// @ts-ignore
|
||||
this.setState({
|
||||
isNotificationOpen: false
|
||||
});
|
||||
@@ -189,8 +183,6 @@ export class HighlightButton extends AbstractHighlightButton<Props, IState> {
|
||||
_visible,
|
||||
classes,
|
||||
t
|
||||
|
||||
// @ts-ignore
|
||||
} = this.props;
|
||||
|
||||
if (!_visible) {
|
||||
@@ -212,7 +204,6 @@ export class HighlightButton extends AbstractHighlightButton<Props, IState> {
|
||||
id = 'highlightMeetingLabel'
|
||||
onClick = { this._onClick } />
|
||||
</Tooltip>
|
||||
{/* @ts-ignore */}
|
||||
{this.state.isNotificationOpen && (
|
||||
<div className = { classes.highlightNotification }>
|
||||
{t('recording.highlightMomentDisabled')}
|
||||
@@ -228,5 +219,4 @@ export class HighlightButton extends AbstractHighlightButton<Props, IState> {
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
export default withStyles(styles)(translate(connect(_abstractMapStateToProps)(HighlightButton)));
|
||||
|
||||
@@ -9,8 +9,8 @@ import AbstractRecordButton, {
|
||||
_mapStateToProps as _abstractMapStateToProps
|
||||
} from '../AbstractRecordButton';
|
||||
|
||||
// @ts-ignore
|
||||
import { StartRecordingDialog, StopRecordingDialog } from './index';
|
||||
import StartRecordingDialog from './StartRecordingDialog';
|
||||
import StopRecordingDialog from './StopRecordingDialog';
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { translate } from '../../../../base/i18n';
|
||||
import { IReduxState } from '../../../../app/types';
|
||||
import { translate } from '../../../../base/i18n/functions';
|
||||
import Dialog from '../../../../base/ui/components/web/Dialog';
|
||||
import { toggleScreenshotCaptureSummary } from '../../../../screenshot-capture';
|
||||
import { toggleScreenshotCaptureSummary } from '../../../../screenshot-capture/actions';
|
||||
import { isScreenshotCaptureEnabled } from '../../../../screenshot-capture/functions';
|
||||
import { RECORDING_TYPES } from '../../../constants';
|
||||
import AbstractStartRecordingDialog, {
|
||||
@@ -21,8 +22,6 @@ import StartRecordingDialogContent from './StartRecordingDialogContent';
|
||||
*/
|
||||
class StartRecordingDialog extends AbstractStartRecordingDialog {
|
||||
|
||||
isStartRecordingDisabled: () => boolean;
|
||||
|
||||
/**
|
||||
* Disables start recording button.
|
||||
*
|
||||
@@ -104,12 +103,6 @@ class StartRecordingDialog extends AbstractStartRecordingDialog {
|
||||
dispatch(toggleScreenshotCaptureSummary(true));
|
||||
}
|
||||
}
|
||||
|
||||
_areIntegrationsEnabled: () => boolean;
|
||||
_onSubmit: () => boolean;
|
||||
_onSelectedRecordingServiceChanged: (string) => void;
|
||||
_onSharingSettingChanged: () => void;
|
||||
_onLocalRecordingSelfChange: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,7 +111,7 @@ class StartRecordingDialog extends AbstractStartRecordingDialog {
|
||||
* @param {Object} state - Redux state.
|
||||
* @returns {Object}
|
||||
*/
|
||||
function mapStateToProps(state) {
|
||||
function mapStateToProps(state: IReduxState) {
|
||||
return {
|
||||
...abstractMapStateToProps(state),
|
||||
_screenshotCaptureEnabled: isScreenshotCaptureEnabled(state, true, false)
|
||||
@@ -25,7 +25,6 @@ import {
|
||||
ICON_INFO,
|
||||
ICON_USERS,
|
||||
LOCAL_RECORDING
|
||||
// @ts-ignore
|
||||
} from '../styles.web';
|
||||
|
||||
|
||||
@@ -122,7 +121,6 @@ class StartRecordingDialogContent extends AbstractStartRecordingDialogContent<IP
|
||||
onSharingSettingChanged,
|
||||
sharingSetting,
|
||||
t
|
||||
// @ts-ignore
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
|
||||
@@ -3,9 +3,7 @@ import { connect } from 'react-redux';
|
||||
|
||||
import { translate } from '../../../../base/i18n/functions';
|
||||
import Dialog from '../../../../base/ui/components/web/Dialog';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import { toggleScreenshotCaptureSummary } from '../../../../screenshot-capture';
|
||||
import { toggleScreenshotCaptureSummary } from '../../../../screenshot-capture/actions';
|
||||
import AbstractStopRecordingDialog, {
|
||||
IProps,
|
||||
_mapStateToProps
|
||||
|
||||
@@ -38,14 +38,12 @@ class RecordingLabel extends AbstractRecordingLabel {
|
||||
* @inheritdoc
|
||||
*/
|
||||
_renderLabel() {
|
||||
// @ts-ignore
|
||||
if (this.props._status !== JitsiRecordingConstants.status.ON) {
|
||||
// Since there are no expanded labels on web, we only render this
|
||||
// label when the recording status is ON.
|
||||
return null;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const { classes, mode, t } = this.props;
|
||||
const isRecording = mode === JitsiRecordingConstants.mode.FILE;
|
||||
const icon = isRecording ? IconRecord : IconSites;
|
||||
@@ -63,5 +61,4 @@ class RecordingLabel extends AbstractRecordingLabel {
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
export default withStyles(styles)(translate(connect(_mapStateToProps)(RecordingLabel)));
|
||||
|
||||
@@ -1,48 +1,43 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import { WithTranslation } from 'react-i18next';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { translate, translateToHTML } from '../../../base/i18n';
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { translate, translateToHTML } from '../../../base/i18n/functions';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link RecordingLimitNotificationDescription}.
|
||||
*/
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The limit of time in minutes for the recording.
|
||||
*/
|
||||
_limit: number,
|
||||
interface IProps extends WithTranslation {
|
||||
|
||||
/**
|
||||
* The name of the app with unlimited recordings.
|
||||
*/
|
||||
_appName: string,
|
||||
_appName?: string;
|
||||
|
||||
/**
|
||||
* The URL to the app with unlimited recordings.
|
||||
*/
|
||||
_appURL: string,
|
||||
_appURL?: string;
|
||||
|
||||
/**
|
||||
* The limit of time in minutes for the recording.
|
||||
*/
|
||||
_limit?: number;
|
||||
|
||||
/**
|
||||
* True if the notification is related to the livestreaming and false if not.
|
||||
*/
|
||||
isLiveStreaming: Boolean,
|
||||
|
||||
/**
|
||||
* Invoked to obtain translated strings.
|
||||
*/
|
||||
t: Function
|
||||
};
|
||||
isLiveStreaming: Boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* A component that renders the description of the notification for the recording initiator.
|
||||
*
|
||||
* @param {Props} props - The props of the component.
|
||||
* @param {IProps} props - The props of the component.
|
||||
* @returns {Component}
|
||||
*/
|
||||
function RecordingLimitNotificationDescription(props: Props) {
|
||||
function RecordingLimitNotificationDescription(props: IProps) {
|
||||
const { _limit, _appName, _appURL, isLiveStreaming, t } = props;
|
||||
|
||||
return (
|
||||
@@ -65,9 +60,9 @@ function RecordingLimitNotificationDescription(props: Props) {
|
||||
* Maps part of the Redux state to the props of this component.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @returns {Props}
|
||||
* @returns {IProps}
|
||||
*/
|
||||
function _mapStateToProps(state): $Shape<Props> {
|
||||
function _mapStateToProps(state: IReduxState) {
|
||||
const { recordingLimit = {} } = state['features/base/config'];
|
||||
const { limit: _limit, appName: _appName, appURL: _appURL } = recordingLimit;
|
||||
|
||||
@@ -210,9 +210,7 @@ export function processPermissionRequestReply(participantId: string, event: any)
|
||||
const virtualScreenshareParticipant = getVirtualScreenshareParticipantByOwnerId(state, participantId);
|
||||
const pinnedId = pinnedParticipant?.id;
|
||||
|
||||
// @ts-ignore
|
||||
if (virtualScreenshareParticipant?.id && pinnedId !== virtualScreenshareParticipant?.id) {
|
||||
// @ts-ignore
|
||||
dispatch(pinParticipant(virtualScreenshareParticipant?.id));
|
||||
} else if (!virtualScreenshareParticipant?.id && pinnedId !== participantId) {
|
||||
dispatch(pinParticipant(participantId));
|
||||
@@ -563,7 +561,7 @@ export function grant(participantId: string) {
|
||||
true,
|
||||
false,
|
||||
{ desktopSharingSources: [ 'screen' ] }
|
||||
)) // @ts-ignore
|
||||
))
|
||||
.then(() => dispatch(sendStartRequest()));
|
||||
}
|
||||
|
||||
|
||||
@@ -154,8 +154,7 @@ const keyCodeToKey = {
|
||||
* Generate codes for digit keys (0-9).
|
||||
*/
|
||||
for (let i = 0; i < 10; i++) {
|
||||
// @ts-ignore
|
||||
keyCodeToKey[i + 48] = `${i}`;
|
||||
keyCodeToKey[(i + 48) as keyof typeof keyCodeToKey] = `${i}`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,7 +30,6 @@ export function _cancelPasswordRequiredPrompt(conference: any) {
|
||||
// unload and clean of the connection.
|
||||
APP.API.notifyReadyToClose();
|
||||
|
||||
// @ts-ignore
|
||||
dispatch(maybeRedirectToWelcomePage());
|
||||
|
||||
return;
|
||||
|
||||
@@ -12,22 +12,22 @@ interface IProps {
|
||||
/**
|
||||
* The id of the record.
|
||||
*/
|
||||
id: string;
|
||||
id?: string;
|
||||
|
||||
/**
|
||||
* The name of the record.
|
||||
*/
|
||||
name: string;
|
||||
name?: string;
|
||||
|
||||
/**
|
||||
* The handler for the click event.
|
||||
*/
|
||||
onClick: (e?: React.MouseEvent) => void;
|
||||
onClick?: (e?: React.MouseEvent) => void;
|
||||
|
||||
/**
|
||||
* The type of the record.
|
||||
*/
|
||||
type: string;
|
||||
type?: string;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles()(theme => {
|
||||
@@ -99,7 +99,7 @@ export const RecordItem = ({
|
||||
<div
|
||||
className = { classes.recordType }
|
||||
key = { type }>
|
||||
{t(RECORD_TYPE[type].label)}
|
||||
{t(RECORD_TYPE[type ?? ''].label)}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
@@ -176,9 +176,7 @@ function SalesforceLinkDialog() {
|
||||
const renderSelection = () => (
|
||||
<div>
|
||||
<div className = { classes.recordInfo }>
|
||||
{/* @ts-ignore */}
|
||||
<RecordItem { ...selectedRecord } />
|
||||
{/* @ts-ignore */}
|
||||
{selectedRecordOwner && <RecordItem { ...selectedRecordOwner } />}
|
||||
{hasDetailsErrors && renderDetailsErrors()}
|
||||
</div>
|
||||
|
||||
@@ -18,11 +18,17 @@ import {
|
||||
searchSessionRecords
|
||||
} from './functions';
|
||||
|
||||
interface ISelectedRecord {
|
||||
id: string;
|
||||
name: string;
|
||||
onClick: (e?: React.MouseEvent) => void;
|
||||
type: string;
|
||||
}
|
||||
|
||||
export const useSalesforceLinkDialog = () => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const [ selectedRecord, setSelectedRecord ] = useState<{
|
||||
id: string; name: string; onClick: Function; type: string; } | null>(null);
|
||||
const [ selectedRecord, setSelectedRecord ] = useState<ISelectedRecord | null>(null);
|
||||
const [ selectedRecordOwner, setSelectedRecordOwner ] = useState<{
|
||||
id: string; name: string; type: string; } | null>(null);
|
||||
const [ records, setRecords ] = useState([]);
|
||||
|
||||
@@ -58,7 +58,7 @@ export function startAudioScreenShareFlow() {
|
||||
const audioOnlySharing = isAudioOnlySharing(state);
|
||||
|
||||
// If we're already in a normal screen sharing session, warn the user.
|
||||
if (isScreenVideoShared(state)) { // @ts-ignore
|
||||
if (isScreenVideoShared(state)) {
|
||||
dispatch(openDialog(ShareMediaWarningDialog, { _isAudioScreenShareWarning: true }));
|
||||
|
||||
return;
|
||||
@@ -76,7 +76,6 @@ export function startAudioScreenShareFlow() {
|
||||
return;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
dispatch(openDialog(ShareAudioDialog));
|
||||
};
|
||||
}
|
||||
@@ -94,7 +93,7 @@ export function startScreenShareFlow(enabled: boolean) {
|
||||
const audioOnlySharing = isAudioOnlySharing(state);
|
||||
|
||||
// If we're in an audio screen sharing session, warn the user.
|
||||
if (audioOnlySharing) { // @ts-ignore
|
||||
if (audioOnlySharing) {
|
||||
dispatch(openDialog(ShareMediaWarningDialog, { _isAudioScreenShareWarning: false }));
|
||||
|
||||
return;
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
// @flow
|
||||
|
||||
import resemble from 'resemblejs';
|
||||
import 'image-capture';
|
||||
import './createImageBitmap';
|
||||
|
||||
import { createScreensharingCaptureTakenEvent, sendAnalytics } from '../analytics';
|
||||
import { getCurrentConference } from '../base/conference';
|
||||
import { getLocalParticipant, getRemoteParticipants } from '../base/participants';
|
||||
import { createScreensharingCaptureTakenEvent } from '../analytics/AnalyticsEvents';
|
||||
import { sendAnalytics } from '../analytics/functions';
|
||||
import { IReduxState } from '../app/types';
|
||||
import { getCurrentConference } from '../base/conference/functions';
|
||||
import { getLocalParticipant, getRemoteParticipants } from '../base/participants/functions';
|
||||
import { ITrack } from '../base/tracks/types';
|
||||
import { extractFqnFromPath } from '../dynamic-branding/functions.any';
|
||||
|
||||
import {
|
||||
@@ -16,35 +17,34 @@ import {
|
||||
POLL_INTERVAL,
|
||||
SET_INTERVAL
|
||||
} from './constants';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import { processScreenshot } from './processScreenshot';
|
||||
import { timerWorkerScript } from './worker';
|
||||
|
||||
declare var interfaceConfig: Object;
|
||||
declare var ImageCapture: any;
|
||||
declare let ImageCapture: any;
|
||||
|
||||
/**
|
||||
* Effect that wraps {@code MediaStream} adding periodic screenshot captures.
|
||||
* Manipulates the original desktop stream and performs custom processing operations, if implemented.
|
||||
*/
|
||||
export default class ScreenshotCaptureSummary {
|
||||
_state: Object;
|
||||
_state: IReduxState;
|
||||
_currentCanvas: HTMLCanvasElement;
|
||||
_currentCanvasContext: CanvasRenderingContext2D;
|
||||
_handleWorkerAction: Function;
|
||||
_initScreenshotCapture: Function;
|
||||
_currentCanvasContext: CanvasRenderingContext2D | null;
|
||||
_initializedRegion: boolean;
|
||||
_imageCapture: any;
|
||||
_streamWorker: Worker;
|
||||
_streamHeight: any;
|
||||
_streamWidth: any;
|
||||
_storedImageData: ImageData;
|
||||
_storedImageData?: ImageData;
|
||||
|
||||
/**
|
||||
* Initializes a new {@code ScreenshotCaptureEffect} instance.
|
||||
*
|
||||
* @param {Object} state - The redux state.
|
||||
*/
|
||||
constructor(state: Object) {
|
||||
constructor(state: IReduxState) {
|
||||
this._state = state;
|
||||
this._currentCanvas = document.createElement('canvas');
|
||||
this._currentCanvasContext = this._currentCanvas.getContext('2d');
|
||||
@@ -66,7 +66,7 @@ export default class ScreenshotCaptureSummary {
|
||||
async _initRegionSelection() {
|
||||
const { _screenshotHistoryRegionUrl } = this._state['features/base/config'];
|
||||
const conference = getCurrentConference(this._state);
|
||||
const sessionId = conference.getMeetingUniqueId();
|
||||
const sessionId = conference?.getMeetingUniqueId();
|
||||
const { jwt } = this._state['features/base/jwt'];
|
||||
|
||||
if (!_screenshotHistoryRegionUrl) {
|
||||
@@ -92,7 +92,7 @@ export default class ScreenshotCaptureSummary {
|
||||
* @returns {Promise} - Promise that resolves once effect has started or rejects if the
|
||||
* videoType parameter is not desktop.
|
||||
*/
|
||||
async start(track: Object) {
|
||||
async start(track: ITrack) {
|
||||
const { videoType } = track;
|
||||
const stream = track.getOriginalStream();
|
||||
|
||||
@@ -136,8 +136,8 @@ export default class ScreenshotCaptureSummary {
|
||||
async _initScreenshotCapture() {
|
||||
const imageBitmap = await this._imageCapture.grabFrame();
|
||||
|
||||
this._currentCanvasContext.drawImage(imageBitmap, 0, 0, this._streamWidth, this._streamHeight);
|
||||
const imageData = this._currentCanvasContext.getImageData(0, 0, this._streamWidth, this._streamHeight);
|
||||
this._currentCanvasContext?.drawImage(imageBitmap, 0, 0, this._streamWidth, this._streamHeight);
|
||||
const imageData = this._currentCanvasContext?.getImageData(0, 0, this._streamWidth, this._streamHeight);
|
||||
|
||||
this._storedImageData = imageData;
|
||||
this._streamWorker.postMessage({
|
||||
@@ -153,7 +153,7 @@ export default class ScreenshotCaptureSummary {
|
||||
* @param {EventHandler} message - Message received from the Worker.
|
||||
* @returns {void}
|
||||
*/
|
||||
_handleWorkerAction(message: Object) {
|
||||
_handleWorkerAction(message: { data: { id: number; }; }) {
|
||||
return message.data.id === INTERVAL_TIMEOUT && this._handleScreenshot();
|
||||
}
|
||||
|
||||
@@ -164,20 +164,20 @@ export default class ScreenshotCaptureSummary {
|
||||
* @param {ImageData} imageData - The image data of the new screenshot.
|
||||
* @returns {void}
|
||||
*/
|
||||
_doProcessScreenshot(imageData) {
|
||||
_doProcessScreenshot(imageData?: ImageData) {
|
||||
sendAnalytics(createScreensharingCaptureTakenEvent());
|
||||
|
||||
const conference = getCurrentConference(this._state);
|
||||
const sessionId = conference.getMeetingUniqueId();
|
||||
const sessionId = conference?.getMeetingUniqueId();
|
||||
const { connection } = this._state['features/base/connection'];
|
||||
const jid = connection.getJid();
|
||||
const jid = connection?.getJid();
|
||||
const timestamp = Date.now();
|
||||
const { jwt } = this._state['features/base/jwt'];
|
||||
const meetingFqn = extractFqnFromPath();
|
||||
const remoteParticipants = getRemoteParticipants(this._state);
|
||||
const participants = [];
|
||||
|
||||
participants.push(getLocalParticipant(this._state).id);
|
||||
participants.push(getLocalParticipant(this._state)?.id);
|
||||
remoteParticipants.forEach(p => participants.push(p.id));
|
||||
this._storedImageData = imageData;
|
||||
|
||||
@@ -200,11 +200,11 @@ export default class ScreenshotCaptureSummary {
|
||||
async _handleScreenshot() {
|
||||
const imageBitmap = await this._imageCapture.grabFrame();
|
||||
|
||||
this._currentCanvasContext.drawImage(imageBitmap, 0, 0, this._streamWidth, this._streamHeight);
|
||||
const imageData = this._currentCanvasContext.getImageData(0, 0, this._streamWidth, this._streamHeight);
|
||||
this._currentCanvasContext?.drawImage(imageBitmap, 0, 0, this._streamWidth, this._streamHeight);
|
||||
const imageData = this._currentCanvasContext?.getImageData(0, 0, this._streamWidth, this._streamHeight);
|
||||
|
||||
resemble(imageData)
|
||||
.compareTo(this._storedImageData)
|
||||
resemble(imageData ?? '')
|
||||
.compareTo(this._storedImageData ?? '')
|
||||
.setReturnEarlyThreshold(PERCENTAGE_LOWER_BOUND)
|
||||
.onComplete(resultData => {
|
||||
if (resultData.rawMisMatchPercentage > PERCENTAGE_LOWER_BOUND) {
|
||||
@@ -3,8 +3,6 @@ import { getMultipleVideoSendingSupportFeatureFlag } from '../base/config/functi
|
||||
import { getLocalJitsiDesktopTrack, getLocalJitsiVideoTrack } from '../base/tracks/functions';
|
||||
|
||||
import { SET_SCREENSHOT_CAPTURE } from './actionTypes';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import { createScreenshotCaptureSummary } from './functions';
|
||||
import logger from './logger';
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @flow
|
||||
|
||||
import { IReduxState } from '../app/types';
|
||||
import { IStateful } from '../base/app/types';
|
||||
import { JitsiRecordingConstants } from '../base/lib-jitsi-meet';
|
||||
import { toState } from '../base/redux';
|
||||
import { toState } from '../base/redux/functions';
|
||||
import { getActiveSession } from '../recording/functions';
|
||||
import { isScreenVideoShared } from '../screen-share/functions';
|
||||
|
||||
@@ -14,7 +14,7 @@ import ScreenshotCaptureSummary from './ScreenshotCaptureSummary';
|
||||
* {@code getState} function.
|
||||
* @returns {Promise<ScreenshotCapture>}
|
||||
*/
|
||||
export function createScreenshotCaptureSummary(stateful: Object | Function) {
|
||||
export function createScreenshotCaptureSummary(stateful: IStateful) {
|
||||
if (!MediaStreamTrack.prototype.getSettings && !MediaStreamTrack.prototype.getConstraints) {
|
||||
return Promise.reject(new Error('ScreenshotCaptureSummary not supported!'));
|
||||
}
|
||||
@@ -30,7 +30,7 @@ export function createScreenshotCaptureSummary(stateful: Object | Function) {
|
||||
* @param {boolean} checkRecording - Whether to check is recording is on.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isScreenshotCaptureEnabled(state: Object, checkSharing, checkRecording) {
|
||||
export function isScreenshotCaptureEnabled(state: IReduxState, checkSharing?: boolean, checkRecording?: boolean) {
|
||||
const { screenshotCapture } = state['features/base/config'];
|
||||
|
||||
if (!screenshotCapture?.enabled) {
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
import {
|
||||
CLEAR_INTERVAL,
|
||||
INTERVAL_TIMEOUT,
|
||||
@@ -27,4 +25,5 @@ const code = `
|
||||
};
|
||||
`;
|
||||
|
||||
// @ts-ignore
|
||||
export const timerWorkerScript = URL.createObjectURL(new Blob([ code ], { type: 'application/javascript' }));
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable lines-around-comment */
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
@@ -8,13 +7,10 @@ import { IJitsiConference } from '../../../../base/conference/reducer';
|
||||
import { getSecurityUiConfig } from '../../../../base/config/functions.any';
|
||||
import { isLocalParticipantModerator } from '../../../../base/participants/functions';
|
||||
import Dialog from '../../../../base/ui/components/web/Dialog';
|
||||
// @ts-ignore
|
||||
import { E2EESection } from '../../../../e2ee/components';
|
||||
// @ts-ignore
|
||||
import { LobbySection } from '../../../../lobby';
|
||||
import E2EESection from '../../../../e2ee/components/E2EESection';
|
||||
import LobbySection from '../../../../lobby/components/web/LobbySection';
|
||||
|
||||
import PasswordSection from './PasswordSection';
|
||||
/* eslint-enable lines-around-comment */
|
||||
|
||||
export interface INotifyClick {
|
||||
key: string;
|
||||
|
||||
@@ -9,19 +9,11 @@ import { translate } from '../../../base/i18n/functions';
|
||||
import { withPixelLineHeight } from '../../../base/styles/functions.web';
|
||||
import Button from '../../../base/ui/components/web/Button';
|
||||
import Spinner from '../../../base/ui/components/web/Spinner';
|
||||
import {
|
||||
CALENDAR_TYPE,
|
||||
MicrosoftSignInButton,
|
||||
bootstrapCalendarIntegration,
|
||||
clearCalendarIntegration,
|
||||
isCalendarEnabled,
|
||||
signIn
|
||||
|
||||
// @ts-ignore
|
||||
} from '../../../calendar-sync';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import { GoogleSignInButton } from '../../../google-api';
|
||||
import { bootstrapCalendarIntegration, clearCalendarIntegration, signIn } from '../../../calendar-sync/actions';
|
||||
import MicrosoftSignInButton from '../../../calendar-sync/components/MicrosoftSignInButton';
|
||||
import { CALENDAR_TYPE } from '../../../calendar-sync/constants';
|
||||
import { isCalendarEnabled } from '../../../calendar-sync/functions';
|
||||
import GoogleSignInButton from '../../../google-api/components/GoogleSignInButton';
|
||||
import logger from '../../logger';
|
||||
|
||||
/**
|
||||
|
||||
@@ -129,7 +129,6 @@ class AbstractVideoManager extends PureComponent<IProps> {
|
||||
this.throttledFireUpdateSharedVideoEvent = throttle(this.fireUpdateSharedVideoEvent.bind(this), 5000);
|
||||
|
||||
// selenium tests handler
|
||||
// @ts-ignore
|
||||
window._sharedVideoPlayer = this;
|
||||
}
|
||||
|
||||
|
||||
@@ -219,8 +219,9 @@ const SpeakerStats = () => {
|
||||
showFaceExpressions && !displaySwitch && dispatch(toggleFaceExpressions());
|
||||
}, [ clientWidth ]);
|
||||
|
||||
// @ts-ignore
|
||||
useEffect(() => () => dispatch(resetSearchCriteria()), []);
|
||||
useEffect(() => () => {
|
||||
dispatch(resetSearchCriteria());
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
|
||||
@@ -5,10 +5,8 @@ import { connect, useDispatch } from 'react-redux';
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
import { IReduxState } from '../../app/types';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import { TRANSLATION_LANGUAGES, TRANSLATION_LANGUAGES_HEAD } from '../../base/i18n';
|
||||
import { translate, translateToHTML } from '../../base/i18n/functions';
|
||||
import { TRANSLATION_LANGUAGES, TRANSLATION_LANGUAGES_HEAD } from '../../base/i18n/i18next';
|
||||
import Dialog from '../../base/ui/components/web/Dialog';
|
||||
import { openSettingsDialog } from '../../settings/actions';
|
||||
import { SETTINGS_TABS } from '../../settings/constants';
|
||||
|
||||
@@ -6,9 +6,7 @@ import { openDialog } from '../../../base/dialog/actions';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
import { IconCloudUpload } from '../../../base/icons/svg';
|
||||
import AbstractButton, { IProps as AbstractButtonProps } from '../../../base/toolbox/components/AbstractButton';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import { SalesforceLinkDialog } from '../../../salesforce/components';
|
||||
import SalesforceLinkDialog from '../../../salesforce/components/web/SalesforceLinkDialog';
|
||||
|
||||
/**
|
||||
* Implementation of a button for opening the Salesforce link dialog.
|
||||
|
||||
@@ -5,9 +5,8 @@ import { makeStyles } from 'tss-react/mui';
|
||||
import { createToolbarEvent } from '../../../analytics/AnalyticsEvents';
|
||||
import { sendAnalytics } from '../../../analytics/functions';
|
||||
import Popover from '../../../base/popover/components/Popover.web';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import { ReactionEmoji, ReactionsMenu } from '../../../reactions/components';
|
||||
import ReactionEmoji from '../../../reactions/components/web/ReactionEmoji';
|
||||
import ReactionsMenu from '../../../reactions/components/web/ReactionsMenu';
|
||||
import { REACTIONS_MENU_HEIGHT } from '../../../reactions/constants';
|
||||
import { getReactionsQueue } from '../../../reactions/functions.any';
|
||||
import { DRAWER_MAX_HEIGHT } from '../../constants';
|
||||
|
||||
@@ -86,14 +86,9 @@ function _setFullScreen(next: Function, action: AnyAction) {
|
||||
if (typeof document.exitFullscreen === 'function') {
|
||||
document.exitFullscreen();
|
||||
|
||||
// @ts-ignore
|
||||
} else if (typeof document.mozCancelFullScreen === 'function') {
|
||||
// @ts-ignore
|
||||
document.mozCancelFullScreen();
|
||||
|
||||
// @ts-ignore
|
||||
} else if (typeof document.webkitExitFullscreen === 'function') {
|
||||
// @ts-ignore
|
||||
document.webkitExitFullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,9 @@
|
||||
"react/features/no-audio-signal",
|
||||
"react/features/noise-suppression",
|
||||
"react/features/old-client-notification",
|
||||
"react/features/remote-control",
|
||||
"react/features/screen-share",
|
||||
"react/features/screenshot-capture",
|
||||
"react/features/stream-effects/noise-suppression",
|
||||
"react/features/stream-effects/rnnoise",
|
||||
"react/features/virtual-background",
|
||||
|
||||
Reference in New Issue
Block a user