diff --git a/css/_audio-preview.scss b/css/_audio-preview.scss index a7caabd27..293bc48dc 100644 --- a/css/_audio-preview.scss +++ b/css/_audio-preview.scss @@ -4,7 +4,7 @@ &-content { position: relative; right: auto; - margin-bottom: 8px; + margin-bottom: 4px; max-height: 456px; overflow: auto; width: 300px; diff --git a/css/_video-preview.scss b/css/_video-preview.scss index 0a4d6d31b..7745e4933 100644 --- a/css/_video-preview.scss +++ b/css/_video-preview.scss @@ -3,49 +3,38 @@ display: inline-block; &-container { - max-height: 344px; - background: $menuBG; - border-radius: 3px; + max-height: 456px; overflow: auto; - padding: 8px; - margin-bottom: 8px; + margin-bottom: 4px; + position: relative; + right: auto; } &-entry { cursor: pointer; - height: 168px; - margin-bottom: 8px; + height: 138px; + width: 244px; position: relative; - width: 284px; + margin: 0 7px 4px; + border-radius: 6px; + box-sizing: border-box; + overflow: hidden; &:last-child { margin-bottom: 0; } &--selected { - border: 3px solid #31B76A; - border-radius: 3px; - cursor: default; - height: 162px; - width: 278px; + border: 2px solid #4687ED; } } &-video { - border-radius: 3px; height: 100%; object-fit: cover; width: 100%; } - &-overlay { - background: rgba(42, 58, 75, 0.6); - height: 100%; - position: absolute; - width: 100%; - z-index: 1; - } - &-error { align-items: center; display: flex; @@ -56,23 +45,22 @@ } &-label { - bottom: 8px; - color: #fff; position: absolute; - width: 100%; + bottom: 0; + left: 0; + right: 0; + max-width: 100%; + padding: 8px; z-index: 2; - &-container { - margin: 0 16px; - } - &-text { - background-color: #131519; - border-radius: 3px; - padding: 2px 8px; - font-size: 13px; - line-height: 20px; - margin: 0 auto; + background-color: rgba(0, 0, 0, 0.7); + border-radius: 4px; + padding: 4px 8px; + color: #fff; + font-size: 12px; + line-height: 16px; + font-weight: 600; max-width: calc(100% - 16px); overflow: hidden; text-overflow: ellipsis; @@ -80,8 +68,8 @@ white-space: nowrap; } } - // Override @atlaskit/InlineDialog container which is made with styled components - & > div:nth-child(2) { - padding: 0; + + &-checkbox-container { + padding: 10px 14px; } } diff --git a/lang/main.json b/lang/main.json index 3cd1a46e2..b1d082913 100644 --- a/lang/main.json +++ b/lang/main.json @@ -1286,6 +1286,7 @@ "grantModerator": "Grant Moderator Rights", "hideSelfView": "Hide self view", "kick": "Kick out", + "mirrorVideo": "Mirror my video", "moderator": "Moderator", "mute": "Participant is muted", "muted": "Muted", diff --git a/react/features/base/media/components/Video.web.js b/react/features/base/media/components/Video.web.ts similarity index 80% rename from react/features/base/media/components/Video.web.js rename to react/features/base/media/components/Video.web.ts index 3243aade8..5f15391ba 100644 --- a/react/features/base/media/components/Video.web.js +++ b/react/features/base/media/components/Video.web.ts @@ -1,5 +1,4 @@ -// @flow - +// @ts-ignore import Video from './web/Video'; export default Video; diff --git a/react/features/base/ui/components/web/ContextMenu.tsx b/react/features/base/ui/components/web/ContextMenu.tsx index 1e4fd6e1c..7b4497275 100644 --- a/react/features/base/ui/components/web/ContextMenu.tsx +++ b/react/features/base/ui/components/web/ContextMenu.tsx @@ -34,6 +34,9 @@ const getComputedOuterHeight = (element: HTMLElement) => { interface IProps { + /** + * ARIA attributes. + */ [key: `aria-${string}`]: string; /** @@ -106,6 +109,11 @@ interface IProps { */ onMouseLeave?: (e?: React.MouseEvent) => void; + /** + * Container role. + */ + role?: string; + /** * Tab index for the menu. */ @@ -167,7 +175,9 @@ const ContextMenu = ({ onDrawerClose, onMouseEnter, onMouseLeave, - tabIndex + role, + tabIndex, + ...aria }: IProps) => { const [ isHidden, setIsHidden ] = useState(true); const containerRef = useRef(null); @@ -225,6 +235,7 @@ const ContextMenu = ({ :
{children}
; diff --git a/react/features/settings/components/web/video/VideoSettingsContent.js b/react/features/settings/components/web/video/VideoSettingsContent.tsx similarity index 56% rename from react/features/settings/components/web/video/VideoSettingsContent.js rename to react/features/settings/components/web/video/VideoSettingsContent.tsx index ca62e9c40..383783590 100644 --- a/react/features/settings/components/web/video/VideoSettingsContent.js +++ b/react/features/settings/components/web/video/VideoSettingsContent.tsx @@ -1,45 +1,63 @@ -// @flow - import React, { Component } from 'react'; +import { WithTranslation } from 'react-i18next'; +import { connect } from 'react-redux'; -import { translate } from '../../../../base/i18n'; -import Video from '../../../../base/media/components/Video'; -import { equals } from '../../../../base/redux'; -import { createLocalVideoTracks } from '../../../functions'; - +import { IReduxState, IStore } from '../../../../app/types'; +import { openDialog } from '../../../../base/dialog/actions'; +import { translate } from '../../../../base/i18n/functions'; +import { IconImage } from '../../../../base/icons/svg'; +import Video from '../../../../base/media/components/Video.web'; +import { equals } from '../../../../base/redux/functions'; +import { updateSettings } from '../../../../base/settings/actions'; +import Checkbox from '../../../../base/ui/components/web/Checkbox'; +import ContextMenu from '../../../../base/ui/components/web/ContextMenu'; +import ContextMenuItem from '../../../../base/ui/components/web/ContextMenuItem'; +import ContextMenuItemGroup from '../../../../base/ui/components/web/ContextMenuItemGroup'; +import VirtualBackgroundDialog from '../../../../virtual-background/components/VirtualBackgroundDialog'; +import { createLocalVideoTracks } from '../../../functions.web'; const videoClassName = 'video-preview-video flipVideoX'; /** * The type of the React {@code Component} props of {@link VideoSettingsContent}. */ -export type Props = { +export interface IProps extends WithTranslation { + + /** + * Callback to change the flip state. + */ + changeFlip: (flip: boolean) => void; /** * The deviceId of the camera device currently being used. */ - currentCameraDeviceId: string, + currentCameraDeviceId: string; + + /** + * Whether or not the local video is flipped. + */ + localFlipX: boolean; + + /** + * Open virtual background dialog. + */ + selectBackground: () => void; /** * Callback invoked to change current camera. */ - setVideoInputDevice: Function, - - /** - * Invoked to obtain translated strings. - */ - t: Function, + setVideoInputDevice: Function; /** * Callback invoked to toggle the settings popup visibility. */ - toggleVideoSettings: Function, + toggleVideoSettings: Function; /** * All the camera device ids currently connected. */ - videoDeviceIds: string[], -}; + videoDeviceIds: string[]; +} /** * The type of the React {@code Component} state of {@link VideoSettingsContent}. @@ -49,7 +67,7 @@ type State = { /** * An array of all the jitsiTracks and eventual errors. */ - trackData: Object[], + trackData: { deviceId: string; error?: string; jitsiTrack: any | null; }[]; }; /** @@ -58,9 +76,8 @@ type State = { * * @augments Component */ -class VideoSettingsContent extends Component { +class VideoSettingsContent extends Component { _componentWasUnmounted: boolean; - _videoContentRef: Object; /** * Initializes a new {@code VideoSettingsContent} instance. @@ -68,10 +85,9 @@ class VideoSettingsContent extends Component { * @param {Object} props - The read-only properties with which the new * instance is to be initialized. */ - constructor(props) { + constructor(props: IProps) { super(props); - this._onEscClick = this._onEscClick.bind(this); - this._videoContentRef = React.createRef(); + this._onToggleFlip = this._onToggleFlip.bind(this); this.state = { trackData: new Array(props.videoDeviceIds.length).fill({ @@ -79,20 +95,16 @@ class VideoSettingsContent extends Component { }) }; } - _onEscClick: (KeyboardEvent) => void; /** - * Click handler for the video entries. + * Toggles local video flip state. * - * @param {KeyboardEvent} event - Esc key click to close the popup. * @returns {void} */ - _onEscClick(event) { - if (event.key === 'Escape') { - event.preventDefault(); - event.stopPropagation(); - this._videoContentRef.current.style.display = 'none'; - } + _onToggleFlip() { + const { localFlipX, changeFlip } = this.props; + + changeFlip(!localFlipX); } /** @@ -122,9 +134,9 @@ class VideoSettingsContent extends Component { * @param {Object[]} trackData - An array of tracks that are to be disposed. * @returns {Promise} */ - _disposeTracks(trackData) { + _disposeTracks(trackData: { jitsiTrack: any; }[]) { trackData.forEach(({ jitsiTrack }) => { - jitsiTrack && jitsiTrack.dispose(); + jitsiTrack?.dispose(); }); } @@ -134,7 +146,7 @@ class VideoSettingsContent extends Component { * @param {string} deviceId - The id of the camera device. * @returns {Function} */ - _onEntryClick(deviceId) { + _onEntryClick(deviceId: string) { return () => { this.props.setVideoInputDevice(deviceId); this.props.toggleVideoSettings(); @@ -148,7 +160,7 @@ class VideoSettingsContent extends Component { * @param {number} index - The index of the entry. * @returns {React$Node} */ - _renderPreviewEntry(data, index) { + _renderPreviewEntry(data: { deviceId: string; error?: string; jitsiTrack: any | null; }, index: number) { const { error, jitsiTrack, deviceId } = data; const { currentCameraDeviceId, t } = this.props; const isSelected = deviceId === currentCameraDeviceId; @@ -167,19 +179,19 @@ class VideoSettingsContent extends Component { ); } - const props: Object = { + const props: any = { className, key, tabIndex }; - const label = jitsiTrack && jitsiTrack.getTrackLabel(); + const label = jitsiTrack?.getTrackLabel(); if (isSelected) { props['aria-checked'] = true; props.className = `${className} video-preview-entry--selected`; } else { props.onClick = this._onEntryClick(deviceId); - props.onKeyPress = e => { + props.onKeyPress = (e: React.KeyboardEvent) => { if (e.key === ' ' || e.key === 'Enter') { e.preventDefault(); props.onClick(); @@ -192,12 +204,10 @@ class VideoSettingsContent extends Component { { ...props } role = 'radio'>
- {label &&
-
- {label}
+ {label &&
+ {label}
}
-