Self view refactor (#10620)
* feat: Drops hide self-view setting from profile tab. * feat: Moves function for disableSelfView value in base/settings. * squash: Drops notification. * feat: Move hide self view option in more tab. * feat: Move hide self view option in more tab. * feat: Adds option to disable self view UI settings. * squash: Disable settings when controlled from config.
This commit is contained in:
parent
2a236b9327
commit
5dee37dd82
|
@ -89,6 +89,9 @@ var config = {
|
||||||
// Disables self-view tile. (hides it from tile view and from filmstrip)
|
// Disables self-view tile. (hides it from tile view and from filmstrip)
|
||||||
// disableSelfView: false,
|
// disableSelfView: false,
|
||||||
|
|
||||||
|
// Disables self-view settings in UI
|
||||||
|
// disableSelfViewSettings: false,
|
||||||
|
|
||||||
// Disables ICE/UDP by filtering out local and remote UDP candidates in
|
// Disables ICE/UDP by filtering out local and remote UDP candidates in
|
||||||
// signalling.
|
// signalling.
|
||||||
// webrtcIceUdpDisable: false,
|
// webrtcIceUdpDisable: false,
|
||||||
|
|
|
@ -857,6 +857,7 @@
|
||||||
"selectAudioOutput": "Audio output",
|
"selectAudioOutput": "Audio output",
|
||||||
"selectCamera": "Camera",
|
"selectCamera": "Camera",
|
||||||
"selectMic": "Microphone",
|
"selectMic": "Microphone",
|
||||||
|
"selfView": "Self view",
|
||||||
"sounds": "Sounds",
|
"sounds": "Sounds",
|
||||||
"speakers": "Speakers",
|
"speakers": "Speakers",
|
||||||
"startAudioMuted": "Everyone starts muted",
|
"startAudioMuted": "Everyone starts muted",
|
||||||
|
|
|
@ -17,6 +17,7 @@ import '../prejoin/middleware';
|
||||||
import '../remote-control/middleware';
|
import '../remote-control/middleware';
|
||||||
import '../screen-share/middleware';
|
import '../screen-share/middleware';
|
||||||
import '../shared-video/middleware';
|
import '../shared-video/middleware';
|
||||||
|
import '../settings/middleware';
|
||||||
import '../talk-while-muted/middleware';
|
import '../talk-while-muted/middleware';
|
||||||
import '../virtual-background/middleware';
|
import '../virtual-background/middleware';
|
||||||
import '../facial-recognition/middleware';
|
import '../facial-recognition/middleware';
|
||||||
|
|
|
@ -115,6 +115,7 @@ export default [
|
||||||
'disableResponsiveTiles',
|
'disableResponsiveTiles',
|
||||||
'disableRtx',
|
'disableRtx',
|
||||||
'disableSelfView',
|
'disableSelfView',
|
||||||
|
'disableSelfViewSettings',
|
||||||
'disableScreensharingVirtualBackground',
|
'disableScreensharingVirtualBackground',
|
||||||
'disableShortcuts',
|
'disableShortcuts',
|
||||||
'disableShowMoreStats',
|
'disableShowMoreStats',
|
||||||
|
|
|
@ -125,12 +125,6 @@ function _setConfig({ dispatch, getState }, next, action) {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action.config.disableSelfView) {
|
|
||||||
dispatch(updateSettings({
|
|
||||||
disableSelfView: true
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(updateConfig(config));
|
dispatch(updateConfig(config));
|
||||||
|
|
||||||
// FIXME On Web we rely on the global 'config' variable which gets altered
|
// FIXME On Web we rely on the global 'config' variable which gets altered
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
// @flow
|
// @flow
|
||||||
import { CONFIG_WHITELIST } from '../config';
|
import { CONFIG_WHITELIST } from '../config';
|
||||||
|
import { getParticipantCount } from '../participants';
|
||||||
import { toState } from '../redux';
|
import { toState } from '../redux';
|
||||||
import { parseURLParams } from '../util';
|
import { parseURLParams } from '../util';
|
||||||
|
|
||||||
|
@ -256,3 +257,23 @@ export function shouldHideShareAudioHelper(state: Object): boolean {
|
||||||
|
|
||||||
return state['features/base/settings'].hideShareAudioHelper;
|
return state['features/base/settings'].hideShareAudioHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether we should hide self view.
|
||||||
|
*
|
||||||
|
* @param {Object} state - Redux state.
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
export function shouldHideSelfView(state: Object) {
|
||||||
|
return getParticipantCount(state) === 1 ? false : getHideSelfView(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the disable self view setting.
|
||||||
|
*
|
||||||
|
* @param {Object} state - Redux state.
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
export function getHideSelfView(state: Object) {
|
||||||
|
return state['features/base/config'].disableSelfView || state['features/base/settings'].disableSelfView;
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import type { Dispatch } from 'redux';
|
import type { Dispatch } from 'redux';
|
||||||
|
|
||||||
import { getLocalParticipant, getParticipantById, pinParticipant } from '../base/participants';
|
import { getLocalParticipant, getParticipantById, pinParticipant } from '../base/participants';
|
||||||
|
import { shouldHideSelfView } from '../base/settings/functions.any';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SET_HORIZONTAL_VIEW_DIMENSIONS,
|
SET_HORIZONTAL_VIEW_DIMENSIONS,
|
||||||
|
@ -23,7 +24,6 @@ import {
|
||||||
calculateThumbnailSizeForTileView,
|
calculateThumbnailSizeForTileView,
|
||||||
calculateThumbnailSizeForVerticalView
|
calculateThumbnailSizeForVerticalView
|
||||||
} from './functions';
|
} from './functions';
|
||||||
import { getDisableSelfView } from './functions.any';
|
|
||||||
|
|
||||||
export * from './actions.any';
|
export * from './actions.any';
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ export function setVerticalViewDimensions() {
|
||||||
return (dispatch: Dispatch<any>, getState: Function) => {
|
return (dispatch: Dispatch<any>, getState: Function) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const { clientHeight = 0, clientWidth = 0 } = state['features/base/responsive-ui'];
|
const { clientHeight = 0, clientWidth = 0 } = state['features/base/responsive-ui'];
|
||||||
const disableSelfView = getDisableSelfView(state);
|
const disableSelfView = shouldHideSelfView(state);
|
||||||
const thumbnails = calculateThumbnailSizeForVerticalView(clientWidth);
|
const thumbnails = calculateThumbnailSizeForVerticalView(clientWidth);
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
|
@ -107,7 +107,7 @@ export function setHorizontalViewDimensions() {
|
||||||
return (dispatch: Dispatch<any>, getState: Function) => {
|
return (dispatch: Dispatch<any>, getState: Function) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const { clientHeight = 0, clientWidth = 0 } = state['features/base/responsive-ui'];
|
const { clientHeight = 0, clientWidth = 0 } = state['features/base/responsive-ui'];
|
||||||
const disableSelfView = getDisableSelfView(state);
|
const disableSelfView = shouldHideSelfView(state);
|
||||||
const thumbnails = calculateThumbnailSizeForHorizontalView(clientHeight);
|
const thumbnails = calculateThumbnailSizeForHorizontalView(clientHeight);
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
|
|
|
@ -7,9 +7,9 @@ import { getLocalParticipant } from '../../../base/participants';
|
||||||
import { Platform } from '../../../base/react';
|
import { Platform } from '../../../base/react';
|
||||||
import { connect } from '../../../base/redux';
|
import { connect } from '../../../base/redux';
|
||||||
import { ASPECT_RATIO_NARROW } from '../../../base/responsive-ui/constants';
|
import { ASPECT_RATIO_NARROW } from '../../../base/responsive-ui/constants';
|
||||||
|
import { shouldHideSelfView } from '../../../base/settings/functions.any';
|
||||||
import { setVisibleRemoteParticipants } from '../../actions';
|
import { setVisibleRemoteParticipants } from '../../actions';
|
||||||
import { isFilmstripVisible, shouldRemoteVideosBeVisible } from '../../functions';
|
import { isFilmstripVisible, shouldRemoteVideosBeVisible } from '../../functions';
|
||||||
import { getDisableSelfView } from '../../functions.any';
|
|
||||||
|
|
||||||
import LocalThumbnail from './LocalThumbnail';
|
import LocalThumbnail from './LocalThumbnail';
|
||||||
import Thumbnail from './Thumbnail';
|
import Thumbnail from './Thumbnail';
|
||||||
|
@ -283,7 +283,7 @@ class Filmstrip extends PureComponent<Props> {
|
||||||
*/
|
*/
|
||||||
function _mapStateToProps(state) {
|
function _mapStateToProps(state) {
|
||||||
const { enabled, remoteParticipants } = state['features/filmstrip'];
|
const { enabled, remoteParticipants } = state['features/filmstrip'];
|
||||||
const disableSelfView = getDisableSelfView(state);
|
const disableSelfView = shouldHideSelfView(state);
|
||||||
const showRemoteVideos = shouldRemoteVideosBeVisible(state);
|
const showRemoteVideos = shouldRemoteVideosBeVisible(state);
|
||||||
const responsiveUI = state['features/base/responsive-ui'];
|
const responsiveUI = state['features/base/responsive-ui'];
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,8 @@ import type { Dispatch } from 'redux';
|
||||||
|
|
||||||
import { getLocalParticipant, getParticipantCountWithFake } from '../../../base/participants';
|
import { getLocalParticipant, getParticipantCountWithFake } from '../../../base/participants';
|
||||||
import { connect } from '../../../base/redux';
|
import { connect } from '../../../base/redux';
|
||||||
|
import { shouldHideSelfView } from '../../../base/settings/functions.any';
|
||||||
import { setVisibleRemoteParticipants } from '../../actions.web';
|
import { setVisibleRemoteParticipants } from '../../actions.web';
|
||||||
import { getDisableSelfView } from '../../functions.any';
|
|
||||||
|
|
||||||
import Thumbnail from './Thumbnail';
|
import Thumbnail from './Thumbnail';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
@ -275,7 +275,7 @@ class TileView extends PureComponent<Props> {
|
||||||
function _mapStateToProps(state) {
|
function _mapStateToProps(state) {
|
||||||
const responsiveUi = state['features/base/responsive-ui'];
|
const responsiveUi = state['features/base/responsive-ui'];
|
||||||
const { remoteParticipants, tileViewDimensions } = state['features/filmstrip'];
|
const { remoteParticipants, tileViewDimensions } = state['features/filmstrip'];
|
||||||
const disableSelfView = getDisableSelfView(state);
|
const disableSelfView = shouldHideSelfView(state);
|
||||||
const { height } = tileViewDimensions.thumbnailSize;
|
const { height } = tileViewDimensions.thumbnailSize;
|
||||||
const { columns } = tileViewDimensions;
|
const { columns } = tileViewDimensions;
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ import { isMobileBrowser } from '../../../base/environment/utils';
|
||||||
import { translate } from '../../../base/i18n';
|
import { translate } from '../../../base/i18n';
|
||||||
import { Icon, IconMenuDown, IconMenuUp } from '../../../base/icons';
|
import { Icon, IconMenuDown, IconMenuUp } from '../../../base/icons';
|
||||||
import { connect } from '../../../base/redux';
|
import { connect } from '../../../base/redux';
|
||||||
|
import { shouldHideSelfView } from '../../../base/settings/functions.any';
|
||||||
import { showToolbox } from '../../../toolbox/actions.web';
|
import { showToolbox } from '../../../toolbox/actions.web';
|
||||||
import { isButtonEnabled, isToolboxVisible } from '../../../toolbox/functions.web';
|
import { isButtonEnabled, isToolboxVisible } from '../../../toolbox/functions.web';
|
||||||
import { LAYOUTS, getCurrentLayout } from '../../../video-layout';
|
import { LAYOUTS, getCurrentLayout } from '../../../video-layout';
|
||||||
|
@ -26,7 +27,6 @@ import {
|
||||||
TOOLBAR_HEIGHT_MOBILE
|
TOOLBAR_HEIGHT_MOBILE
|
||||||
} from '../../constants';
|
} from '../../constants';
|
||||||
import { shouldRemoteVideosBeVisible } from '../../functions';
|
import { shouldRemoteVideosBeVisible } from '../../functions';
|
||||||
import { getDisableSelfView } from '../../functions.any';
|
|
||||||
|
|
||||||
import AudioTracksContainer from './AudioTracksContainer';
|
import AudioTracksContainer from './AudioTracksContainer';
|
||||||
import Thumbnail from './Thumbnail';
|
import Thumbnail from './Thumbnail';
|
||||||
|
@ -580,7 +580,7 @@ function _mapStateToProps(state) {
|
||||||
thumbnailSize: tileViewThumbnailSize
|
thumbnailSize: tileViewThumbnailSize
|
||||||
} = state['features/filmstrip'].tileViewDimensions;
|
} = state['features/filmstrip'].tileViewDimensions;
|
||||||
const _currentLayout = getCurrentLayout(state);
|
const _currentLayout = getCurrentLayout(state);
|
||||||
const disableSelfView = getDisableSelfView(state);
|
const disableSelfView = shouldHideSelfView(state);
|
||||||
|
|
||||||
const { clientHeight, clientWidth } = state['features/base/responsive-ui'];
|
const { clientHeight, clientWidth } = state['features/base/responsive-ui'];
|
||||||
const availableSpace = clientHeight - filmstripHeight;
|
const availableSpace = clientHeight - filmstripHeight;
|
||||||
|
|
|
@ -3,8 +3,8 @@ import React, { Component } from 'react';
|
||||||
import { shouldComponentUpdate } from 'react-window';
|
import { shouldComponentUpdate } from 'react-window';
|
||||||
|
|
||||||
import { connect } from '../../../base/redux';
|
import { connect } from '../../../base/redux';
|
||||||
|
import { shouldHideSelfView } from '../../../base/settings/functions.any';
|
||||||
import { getCurrentLayout, LAYOUTS } from '../../../video-layout';
|
import { getCurrentLayout, LAYOUTS } from '../../../video-layout';
|
||||||
import { getDisableSelfView } from '../../functions.any';
|
|
||||||
|
|
||||||
import Thumbnail from './Thumbnail';
|
import Thumbnail from './Thumbnail';
|
||||||
|
|
||||||
|
@ -118,7 +118,7 @@ function _mapStateToProps(state, ownProps) {
|
||||||
const { remote, local } = state['features/base/participants'];
|
const { remote, local } = state['features/base/participants'];
|
||||||
const remoteParticipantsLength = remoteParticipants.length;
|
const remoteParticipantsLength = remoteParticipants.length;
|
||||||
const { testing = {} } = state['features/base/config'];
|
const { testing = {} } = state['features/base/config'];
|
||||||
const disableSelfView = getDisableSelfView(state);
|
const disableSelfView = shouldHideSelfView(state);
|
||||||
const enableThumbnailReordering = testing.enableThumbnailReordering ?? true;
|
const enableThumbnailReordering = testing.enableThumbnailReordering ?? true;
|
||||||
|
|
||||||
if (_currentLayout === LAYOUTS.TILE_VIEW) {
|
if (_currentLayout === LAYOUTS.TILE_VIEW) {
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import { getParticipantCount } from '../base/participants';
|
|
||||||
|
|
||||||
import { setRemoteParticipants } from './actions';
|
import { setRemoteParticipants } from './actions';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -82,16 +80,3 @@ export function updateRemoteParticipantsOnLeave(store: Object, participantId: ?s
|
||||||
reorderedParticipants.delete(participantId)
|
reorderedParticipants.delete(participantId)
|
||||||
&& store.dispatch(setRemoteParticipants(Array.from(reorderedParticipants)));
|
&& store.dispatch(setRemoteParticipants(Array.from(reorderedParticipants)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the disable self view flag.
|
|
||||||
*
|
|
||||||
* @param {Object} state - Redux state.
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
export function getDisableSelfView(state: Object) {
|
|
||||||
const { disableSelfView } = state['features/base/settings'];
|
|
||||||
const participantsCount = getParticipantCount(state);
|
|
||||||
|
|
||||||
return participantsCount === 1 ? false : disableSelfView;
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { isMobileBrowser } from '../base/environment/utils';
|
||||||
import { getParticipantCountWithFake } from '../base/participants';
|
import { getParticipantCountWithFake } from '../base/participants';
|
||||||
import { StateListenerRegistry, equals } from '../base/redux';
|
import { StateListenerRegistry, equals } from '../base/redux';
|
||||||
import { clientResized } from '../base/responsive-ui';
|
import { clientResized } from '../base/responsive-ui';
|
||||||
|
import { shouldHideSelfView } from '../base/settings';
|
||||||
import { setFilmstripVisible } from '../filmstrip/actions';
|
import { setFilmstripVisible } from '../filmstrip/actions';
|
||||||
import { getParticipantsPaneOpen } from '../participants-pane/functions';
|
import { getParticipantsPaneOpen } from '../participants-pane/functions';
|
||||||
import { setOverflowDrawer } from '../toolbox/actions.web';
|
import { setOverflowDrawer } from '../toolbox/actions.web';
|
||||||
|
@ -30,7 +31,7 @@ StateListenerRegistry.register(
|
||||||
/* selector */ state => {
|
/* selector */ state => {
|
||||||
return {
|
return {
|
||||||
numberOfParticipants: getParticipantCountWithFake(state),
|
numberOfParticipants: getParticipantCountWithFake(state),
|
||||||
disableSelfView: state['features/base/settings'].disableSelfView
|
disableSelfView: shouldHideSelfView(state)
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
/* listener */ (currentState, store) => {
|
/* listener */ (currentState, store) => {
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
import './middleware.any';
|
|
|
@ -1,40 +0,0 @@
|
||||||
/* @flow */
|
|
||||||
|
|
||||||
import { CONFERENCE_JOINED } from '../base/conference';
|
|
||||||
import { MiddlewareRegistry } from '../base/redux';
|
|
||||||
import { openSettingsDialog, SETTINGS_TABS } from '../settings';
|
|
||||||
|
|
||||||
import {
|
|
||||||
showNotification
|
|
||||||
} from './actions';
|
|
||||||
import { NOTIFICATION_TIMEOUT_TYPE } from './constants';
|
|
||||||
|
|
||||||
import './middleware.any';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Middleware that captures actions to display notifications.
|
|
||||||
*
|
|
||||||
* @param {Store} store - The redux store.
|
|
||||||
* @returns {Function}
|
|
||||||
*/
|
|
||||||
MiddlewareRegistry.register(store => next => action => {
|
|
||||||
switch (action.type) {
|
|
||||||
case CONFERENCE_JOINED: {
|
|
||||||
const { dispatch, getState } = store;
|
|
||||||
const { disableSelfView } = getState()['features/base/settings'];
|
|
||||||
|
|
||||||
if (disableSelfView) {
|
|
||||||
dispatch(showNotification({
|
|
||||||
titleKey: 'notify.selfViewTitle',
|
|
||||||
customActionNameKey: [ 'settings.title' ],
|
|
||||||
customActionHandler: [ () =>
|
|
||||||
dispatch(openSettingsDialog(SETTINGS_TABS.PROFILE))
|
|
||||||
]
|
|
||||||
}, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return next(action);
|
|
||||||
});
|
|
|
@ -10,7 +10,6 @@ import {
|
||||||
import { openDialog } from '../base/dialog';
|
import { openDialog } from '../base/dialog';
|
||||||
import { i18next } from '../base/i18n';
|
import { i18next } from '../base/i18n';
|
||||||
import { updateSettings } from '../base/settings';
|
import { updateSettings } from '../base/settings';
|
||||||
import { NOTIFICATION_TIMEOUT_TYPE, showNotification } from '../notifications';
|
|
||||||
import { setPrejoinPageVisibility, setSkipPrejoinIsChanging } from '../prejoin/actions';
|
import { setPrejoinPageVisibility, setSkipPrejoinIsChanging } from '../prejoin/actions';
|
||||||
import { setScreenshareFramerate } from '../screen-share/actions';
|
import { setScreenshareFramerate } from '../screen-share/actions';
|
||||||
|
|
||||||
|
@ -26,8 +25,6 @@ import {
|
||||||
getSoundsTabProps
|
getSoundsTabProps
|
||||||
} from './functions';
|
} from './functions';
|
||||||
|
|
||||||
import { SETTINGS_TABS } from '.';
|
|
||||||
|
|
||||||
declare var APP: Object;
|
declare var APP: Object;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -113,6 +110,10 @@ export function submitMoreTab(newState: Object): Function {
|
||||||
|
|
||||||
dispatch(setScreenshareFramerate(frameRate));
|
dispatch(setScreenshareFramerate(frameRate));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (newState.hideSelfView !== currentState.hideSelfView) {
|
||||||
|
dispatch(updateSettings({ disableSelfView: newState.hideSelfView }));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,19 +164,6 @@ export function submitProfileTab(newState: Object): Function {
|
||||||
if (newState.email !== currentState.email) {
|
if (newState.email !== currentState.email) {
|
||||||
APP.conference.changeLocalEmail(newState.email);
|
APP.conference.changeLocalEmail(newState.email);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newState.disableSelfView !== currentState.disableSelfView) {
|
|
||||||
dispatch(updateSettings({ disableSelfView: newState.disableSelfView }));
|
|
||||||
if (newState.disableSelfView) {
|
|
||||||
dispatch(showNotification({
|
|
||||||
titleKey: 'notify.selfViewTitle',
|
|
||||||
customActionNameKey: [ 'settings.title' ],
|
|
||||||
customActionHandler: [ () =>
|
|
||||||
dispatch(openSettingsDialog(SETTINGS_TABS.PROFILE))
|
|
||||||
]
|
|
||||||
}, NOTIFICATION_TIMEOUT_TYPE.STICKY));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,11 @@ export type Props = {
|
||||||
*/
|
*/
|
||||||
desktopShareFramerates: Array<number>,
|
desktopShareFramerates: Array<number>,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to show hide self view setting.
|
||||||
|
*/
|
||||||
|
disableHideSelfView: boolean,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether or not follow me is currently active (enabled by some other participant).
|
* Whether or not follow me is currently active (enabled by some other participant).
|
||||||
*/
|
*/
|
||||||
|
@ -65,6 +70,11 @@ export type Props = {
|
||||||
*/
|
*/
|
||||||
showPrejoinPage: boolean,
|
showPrejoinPage: boolean,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not to hide self-view screen.
|
||||||
|
*/
|
||||||
|
hideSelfView: boolean,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked to obtain translated strings.
|
* Invoked to obtain translated strings.
|
||||||
*/
|
*/
|
||||||
|
@ -114,6 +124,7 @@ class MoreTab extends AbstractDialogTab<Props, State> {
|
||||||
this._onLanguageItemSelect = this._onLanguageItemSelect.bind(this);
|
this._onLanguageItemSelect = this._onLanguageItemSelect.bind(this);
|
||||||
this._onShowPrejoinPageChanged = this._onShowPrejoinPageChanged.bind(this);
|
this._onShowPrejoinPageChanged = this._onShowPrejoinPageChanged.bind(this);
|
||||||
this._onKeyboardShortcutEnableChanged = this._onKeyboardShortcutEnableChanged.bind(this);
|
this._onKeyboardShortcutEnableChanged = this._onKeyboardShortcutEnableChanged.bind(this);
|
||||||
|
this._onHideSelfViewChanged = this._onHideSelfViewChanged.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -222,6 +233,19 @@ class MoreTab extends AbstractDialogTab<Props, State> {
|
||||||
super._onChange({ keyboardShortcutEnable: checked });
|
super._onChange({ keyboardShortcutEnable: checked });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_onHideSelfViewChanged: (Object) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback invoked to select if hide self view should be enabled.
|
||||||
|
*
|
||||||
|
* @param {Object} e - The key event to handle.
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_onHideSelfViewChanged({ target: { checked } }) {
|
||||||
|
super._onChange({ hideSelfView: checked });
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the React Element for the desktop share frame rate dropdown.
|
* Returns the React Element for the desktop share frame rate dropdown.
|
||||||
*
|
*
|
||||||
|
@ -300,6 +324,31 @@ class MoreTab extends AbstractDialogTab<Props, State> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the React Element for self view setting.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @returns {ReactElement}
|
||||||
|
*/
|
||||||
|
_renderSelfViewCheckbox() {
|
||||||
|
const { hideSelfView, t } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className = 'settings-sub-pane-element'
|
||||||
|
key = 'selfview'>
|
||||||
|
<h2 className = 'mock-atlaskit-label'>
|
||||||
|
{ t('settings.selfView') }
|
||||||
|
</h2>
|
||||||
|
<Checkbox
|
||||||
|
isChecked = { hideSelfView }
|
||||||
|
label = { t('videothumbnail.hideSelfView') }
|
||||||
|
name = 'hide-self-view'
|
||||||
|
onChange = { this._onHideSelfViewChanged } />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the menu item for changing displayed language.
|
* Returns the menu item for changing displayed language.
|
||||||
*
|
*
|
||||||
|
@ -404,7 +453,7 @@ class MoreTab extends AbstractDialogTab<Props, State> {
|
||||||
* @returns {ReactElement}
|
* @returns {ReactElement}
|
||||||
*/
|
*/
|
||||||
_renderSettingsLeft() {
|
_renderSettingsLeft() {
|
||||||
const { showPrejoinSettings } = this.props;
|
const { disableHideSelfView, showPrejoinSettings } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
@ -412,6 +461,7 @@ class MoreTab extends AbstractDialogTab<Props, State> {
|
||||||
key = 'settings-sub-pane-left'>
|
key = 'settings-sub-pane-left'>
|
||||||
{ showPrejoinSettings && this._renderPrejoinScreenSettings() }
|
{ showPrejoinSettings && this._renderPrejoinScreenSettings() }
|
||||||
{ this._renderKeyboardShortcutCheckbox() }
|
{ this._renderKeyboardShortcutCheckbox() }
|
||||||
|
{ !disableHideSelfView && this._renderSelfViewCheckbox() }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import Button from '@atlaskit/button/standard-button';
|
import Button from '@atlaskit/button/standard-button';
|
||||||
import Checkbox from '@atlaskit/checkbox';
|
|
||||||
import { FieldTextStateless } from '@atlaskit/field-text';
|
import { FieldTextStateless } from '@atlaskit/field-text';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
@ -33,11 +32,6 @@ export type Props = {
|
||||||
*/
|
*/
|
||||||
authLogin: string,
|
authLogin: string,
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether or not to hide the self view.
|
|
||||||
*/
|
|
||||||
disableSelfView: boolean,
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The display name to display for the local participant.
|
* The display name to display for the local participant.
|
||||||
*/
|
*/
|
||||||
|
@ -88,7 +82,6 @@ class ProfileTab extends AbstractDialogTab<Props> {
|
||||||
this._onAuthToggle = this._onAuthToggle.bind(this);
|
this._onAuthToggle = this._onAuthToggle.bind(this);
|
||||||
this._onDisplayNameChange = this._onDisplayNameChange.bind(this);
|
this._onDisplayNameChange = this._onDisplayNameChange.bind(this);
|
||||||
this._onEmailChange = this._onEmailChange.bind(this);
|
this._onEmailChange = this._onEmailChange.bind(this);
|
||||||
this._onChange = this._onChange.bind(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_onDisplayNameChange: (Object) => void;
|
_onDisplayNameChange: (Object) => void;
|
||||||
|
@ -117,19 +110,6 @@ class ProfileTab extends AbstractDialogTab<Props> {
|
||||||
super._onChange({ email: value });
|
super._onChange({ email: value });
|
||||||
}
|
}
|
||||||
|
|
||||||
_onChange: (Object) => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Changes the disable self view state.
|
|
||||||
*
|
|
||||||
* @param {Object} e - The key event to handle.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
_onChange({ target }) {
|
|
||||||
super._onChange({ disableSelfView: target.checked });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements React's {@link Component#render()}.
|
* Implements React's {@link Component#render()}.
|
||||||
*
|
*
|
||||||
|
@ -140,7 +120,6 @@ class ProfileTab extends AbstractDialogTab<Props> {
|
||||||
const {
|
const {
|
||||||
authEnabled,
|
authEnabled,
|
||||||
displayName,
|
displayName,
|
||||||
disableSelfView,
|
|
||||||
email,
|
email,
|
||||||
hideEmailInSettings,
|
hideEmailInSettings,
|
||||||
readOnlyName,
|
readOnlyName,
|
||||||
|
@ -175,12 +154,6 @@ class ProfileTab extends AbstractDialogTab<Props> {
|
||||||
value = { email } />
|
value = { email } />
|
||||||
</div>}
|
</div>}
|
||||||
</div>
|
</div>
|
||||||
<br />
|
|
||||||
<Checkbox
|
|
||||||
isChecked = { disableSelfView }
|
|
||||||
label = { t('videothumbnail.hideSelfView') }
|
|
||||||
name = 'disableSelfView'
|
|
||||||
onChange = { this._onChange } />
|
|
||||||
{ authEnabled && this._renderAuth() }
|
{ authEnabled && this._renderAuth() }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -244,6 +244,7 @@ function _mapStateToProps(state) {
|
||||||
...newProps,
|
...newProps,
|
||||||
currentFramerate: tabState.currentFramerate,
|
currentFramerate: tabState.currentFramerate,
|
||||||
currentLanguage: tabState.currentLanguage,
|
currentLanguage: tabState.currentLanguage,
|
||||||
|
hideSelfView: tabState.hideSelfView,
|
||||||
showPrejoinPage: tabState.showPrejoinPage
|
showPrejoinPage: tabState.showPrejoinPage
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
isLocalParticipantModerator
|
isLocalParticipantModerator
|
||||||
} from '../base/participants';
|
} from '../base/participants';
|
||||||
import { toState } from '../base/redux';
|
import { toState } from '../base/redux';
|
||||||
|
import { getHideSelfView } from '../base/settings';
|
||||||
import { parseStandardURIString } from '../base/util';
|
import { parseStandardURIString } from '../base/util';
|
||||||
import { isFollowMeActive } from '../follow-me';
|
import { isFollowMeActive } from '../follow-me';
|
||||||
import { isReactionsEnabled } from '../reactions/functions.any';
|
import { isReactionsEnabled } from '../reactions/functions.any';
|
||||||
|
@ -92,10 +93,15 @@ export function getMoreTabProps(stateful: Object | Function) {
|
||||||
const language = i18next.language || DEFAULT_LANGUAGE;
|
const language = i18next.language || DEFAULT_LANGUAGE;
|
||||||
const configuredTabs = interfaceConfig.SETTINGS_SECTIONS || [];
|
const configuredTabs = interfaceConfig.SETTINGS_SECTIONS || [];
|
||||||
|
|
||||||
|
// when self view is controlled by the config we hide the settings
|
||||||
|
const { disableSelfView, disableSelfViewSettings } = state['features/base/config'];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
currentFramerate: framerate,
|
currentFramerate: framerate,
|
||||||
currentLanguage: language,
|
currentLanguage: language,
|
||||||
desktopShareFramerates: SS_SUPPORTED_FRAMERATES,
|
desktopShareFramerates: SS_SUPPORTED_FRAMERATES,
|
||||||
|
disableHideSelfView: disableSelfViewSettings || disableSelfView,
|
||||||
|
hideSelfView: getHideSelfView(state),
|
||||||
languages: LANGUAGES,
|
languages: LANGUAGES,
|
||||||
showLanguageSettings: configuredTabs.includes('language'),
|
showLanguageSettings: configuredTabs.includes('language'),
|
||||||
showPrejoinPage: !state['features/base/settings'].userSelectedSkipPrejoin,
|
showPrejoinPage: !state['features/base/settings'].userSelectedSkipPrejoin,
|
||||||
|
@ -159,13 +165,11 @@ export function getProfileTabProps(stateful: Object | Function) {
|
||||||
} = state['features/base/conference'];
|
} = state['features/base/conference'];
|
||||||
const { hideEmailInSettings } = state['features/base/config'];
|
const { hideEmailInSettings } = state['features/base/config'];
|
||||||
const localParticipant = getLocalParticipant(state);
|
const localParticipant = getLocalParticipant(state);
|
||||||
const { disableSelfView } = state['features/base/settings'];
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
authEnabled: Boolean(conference && authEnabled),
|
authEnabled: Boolean(conference && authEnabled),
|
||||||
authLogin,
|
authLogin,
|
||||||
displayName: localParticipant.name,
|
displayName: localParticipant.name,
|
||||||
disableSelfView: Boolean(disableSelfView),
|
|
||||||
email: localParticipant.email,
|
email: localParticipant.email,
|
||||||
readOnlyName: isNameReadOnly(state),
|
readOnlyName: isNameReadOnly(state),
|
||||||
hideEmailInSettings
|
hideEmailInSettings
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { MiddlewareRegistry } from '../base/redux';
|
||||||
|
import { getHideSelfView, SETTINGS_UPDATED } from '../base/settings';
|
||||||
|
import { NOTIFICATION_TIMEOUT_TYPE, showNotification } from '../notifications';
|
||||||
|
|
||||||
|
import { openSettingsDialog } from './actions';
|
||||||
|
import { SETTINGS_TABS } from './constants';
|
||||||
|
|
||||||
|
MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
|
||||||
|
const oldValue = getHideSelfView(getState());
|
||||||
|
|
||||||
|
const result = next(action);
|
||||||
|
|
||||||
|
switch (action.type) {
|
||||||
|
case SETTINGS_UPDATED: {
|
||||||
|
const newValue = action.settings.disableSelfView;
|
||||||
|
|
||||||
|
if (newValue !== oldValue && newValue) {
|
||||||
|
dispatch(showNotification({
|
||||||
|
titleKey: 'notify.selfViewTitle',
|
||||||
|
customActionNameKey: [ 'settings.title' ],
|
||||||
|
customActionHandler: [ () =>
|
||||||
|
dispatch(openSettingsDialog(SETTINGS_TABS.MORE))
|
||||||
|
]
|
||||||
|
}, NOTIFICATION_TIMEOUT_TYPE.STICKY));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
});
|
|
@ -8,6 +8,7 @@ import {
|
||||||
pinParticipant,
|
pinParticipant,
|
||||||
getParticipantCountWithFake
|
getParticipantCountWithFake
|
||||||
} from '../base/participants';
|
} from '../base/participants';
|
||||||
|
import { shouldHideSelfView } from '../base/settings/functions.any';
|
||||||
import {
|
import {
|
||||||
ASPECT_RATIO_BREAKPOINT,
|
ASPECT_RATIO_BREAKPOINT,
|
||||||
DEFAULT_MAX_COLUMNS,
|
DEFAULT_MAX_COLUMNS,
|
||||||
|
@ -15,7 +16,6 @@ import {
|
||||||
SINGLE_COLUMN_BREAKPOINT,
|
SINGLE_COLUMN_BREAKPOINT,
|
||||||
TWO_COLUMN_BREAKPOINT
|
TWO_COLUMN_BREAKPOINT
|
||||||
} from '../filmstrip/constants';
|
} from '../filmstrip/constants';
|
||||||
import { getDisableSelfView } from '../filmstrip/functions.any';
|
|
||||||
import { isVideoPlaying } from '../shared-video/functions';
|
import { isVideoPlaying } from '../shared-video/functions';
|
||||||
|
|
||||||
import { LAYOUTS } from './constants';
|
import { LAYOUTS } from './constants';
|
||||||
|
@ -105,7 +105,7 @@ export function getTileViewGridDimensions(state: Object) {
|
||||||
// When in tile view mode, we must discount ourselves (the local participant) because our
|
// When in tile view mode, we must discount ourselves (the local participant) because our
|
||||||
// tile is not visible.
|
// tile is not visible.
|
||||||
const { iAmRecorder } = state['features/base/config'];
|
const { iAmRecorder } = state['features/base/config'];
|
||||||
const disableSelfView = getDisableSelfView(state);
|
const disableSelfView = shouldHideSelfView(state);
|
||||||
const numberOfParticipants = getParticipantCountWithFake(state)
|
const numberOfParticipants = getParticipantCountWithFake(state)
|
||||||
- (iAmRecorder ? 1 : 0)
|
- (iAmRecorder ? 1 : 0)
|
||||||
- (disableSelfView ? 1 : 0);
|
- (disableSelfView ? 1 : 0);
|
||||||
|
|
|
@ -5,9 +5,7 @@ import React, { PureComponent } from 'react';
|
||||||
import ContextMenuItem from '../../../base/components/context-menu/ContextMenuItem';
|
import ContextMenuItem from '../../../base/components/context-menu/ContextMenuItem';
|
||||||
import { translate } from '../../../base/i18n';
|
import { translate } from '../../../base/i18n';
|
||||||
import { connect } from '../../../base/redux';
|
import { connect } from '../../../base/redux';
|
||||||
import { updateSettings } from '../../../base/settings';
|
import { getHideSelfView, updateSettings } from '../../../base/settings';
|
||||||
import { NOTIFICATION_TIMEOUT_TYPE, showNotification } from '../../../notifications';
|
|
||||||
import { openSettingsDialog, SETTINGS_TABS } from '../../../settings';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of the React {@code Component} props of {@link HideSelfViewVideoButton}.
|
* The type of the React {@code Component} props of {@link HideSelfViewVideoButton}.
|
||||||
|
@ -97,15 +95,6 @@ class HideSelfViewVideoButton extends PureComponent<Props> {
|
||||||
dispatch(updateSettings({
|
dispatch(updateSettings({
|
||||||
disableSelfView: !disableSelfView
|
disableSelfView: !disableSelfView
|
||||||
}));
|
}));
|
||||||
if (!disableSelfView) {
|
|
||||||
dispatch(showNotification({
|
|
||||||
titleKey: 'notify.selfViewTitle',
|
|
||||||
customActionNameKey: [ 'settings.title' ],
|
|
||||||
customActionHandler: [ () =>
|
|
||||||
dispatch(openSettingsDialog(SETTINGS_TABS.PROFILE))
|
|
||||||
]
|
|
||||||
}, NOTIFICATION_TIMEOUT_TYPE.STICKY));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,10 +106,8 @@ class HideSelfViewVideoButton extends PureComponent<Props> {
|
||||||
* @returns {Props}
|
* @returns {Props}
|
||||||
*/
|
*/
|
||||||
function _mapStateToProps(state) {
|
function _mapStateToProps(state) {
|
||||||
const { disableSelfView } = state['features/base/config'];
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
disableSelfView: Boolean(disableSelfView)
|
disableSelfView: Boolean(getHideSelfView(state))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ import {
|
||||||
import { Popover } from '../../../base/popover';
|
import { Popover } from '../../../base/popover';
|
||||||
import { connect } from '../../../base/redux';
|
import { connect } from '../../../base/redux';
|
||||||
import { setParticipantContextMenuOpen } from '../../../base/responsive-ui/actions';
|
import { setParticipantContextMenuOpen } from '../../../base/responsive-ui/actions';
|
||||||
|
import { getHideSelfView } from '../../../base/settings';
|
||||||
import { getLocalVideoTrack } from '../../../base/tracks';
|
import { getLocalVideoTrack } from '../../../base/tracks';
|
||||||
import ConnectionIndicatorContent from '../../../connection-indicator/components/web/ConnectionIndicatorContent';
|
import ConnectionIndicatorContent from '../../../connection-indicator/components/web/ConnectionIndicatorContent';
|
||||||
import { getCurrentLayout, LAYOUTS } from '../../../video-layout';
|
import { getCurrentLayout, LAYOUTS } from '../../../video-layout';
|
||||||
|
@ -82,6 +83,11 @@ type Props = {
|
||||||
*/
|
*/
|
||||||
_showConnectionInfo: boolean,
|
_showConnectionInfo: boolean,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to render the hide self view button.
|
||||||
|
*/
|
||||||
|
_showHideSelfViewButton: boolean,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows/hides the local video flip button.
|
* Shows/hides the local video flip button.
|
||||||
*/
|
*/
|
||||||
|
@ -148,8 +154,9 @@ class LocalVideoMenuTriggerButton extends Component<Props> {
|
||||||
const {
|
const {
|
||||||
_localParticipantId,
|
_localParticipantId,
|
||||||
_menuPosition,
|
_menuPosition,
|
||||||
_showConnectionInfo,
|
|
||||||
_overflowDrawer,
|
_overflowDrawer,
|
||||||
|
_showConnectionInfo,
|
||||||
|
_showHideSelfViewButton,
|
||||||
_showLocalVideoFlipButton,
|
_showLocalVideoFlipButton,
|
||||||
buttonVisible,
|
buttonVisible,
|
||||||
classes,
|
classes,
|
||||||
|
@ -169,9 +176,11 @@ class LocalVideoMenuTriggerButton extends Component<Props> {
|
||||||
<FlipLocalVideoButton
|
<FlipLocalVideoButton
|
||||||
className = { _overflowDrawer ? classes.flipText : '' }
|
className = { _overflowDrawer ? classes.flipText : '' }
|
||||||
onClick = { hidePopover } />
|
onClick = { hidePopover } />
|
||||||
<HideSelfViewVideoButton
|
{ _showHideSelfViewButton
|
||||||
className = { _overflowDrawer ? classes.flipText : '' }
|
&& <HideSelfViewVideoButton
|
||||||
onClick = { hidePopover } />
|
className = { _overflowDrawer ? classes.flipText : '' }
|
||||||
|
onClick = { hidePopover } />
|
||||||
|
}
|
||||||
{ isMobileBrowser()
|
{ isMobileBrowser()
|
||||||
&& <ConnectionStatusButton participantId = { _localParticipantId } />
|
&& <ConnectionStatusButton participantId = { _localParticipantId } />
|
||||||
}
|
}
|
||||||
|
@ -249,10 +258,11 @@ class LocalVideoMenuTriggerButton extends Component<Props> {
|
||||||
function _mapStateToProps(state) {
|
function _mapStateToProps(state) {
|
||||||
const currentLayout = getCurrentLayout(state);
|
const currentLayout = getCurrentLayout(state);
|
||||||
const localParticipant = getLocalParticipant(state);
|
const localParticipant = getLocalParticipant(state);
|
||||||
const { disableLocalVideoFlip } = state['features/base/config'];
|
const { disableLocalVideoFlip, disableSelfViewSettings } = state['features/base/config'];
|
||||||
const videoTrack = getLocalVideoTrack(state['features/base/tracks']);
|
const videoTrack = getLocalVideoTrack(state['features/base/tracks']);
|
||||||
const { overflowDrawer } = state['features/toolbox'];
|
const { overflowDrawer } = state['features/toolbox'];
|
||||||
const { showConnectionInfo } = state['features/base/connection'];
|
const { showConnectionInfo } = state['features/base/connection'];
|
||||||
|
const showHideSelfViewButton = !disableSelfViewSettings && !getHideSelfView(state);
|
||||||
|
|
||||||
let _menuPosition;
|
let _menuPosition;
|
||||||
|
|
||||||
|
@ -273,6 +283,7 @@ function _mapStateToProps(state) {
|
||||||
return {
|
return {
|
||||||
_menuPosition,
|
_menuPosition,
|
||||||
_showLocalVideoFlipButton: !disableLocalVideoFlip && videoTrack?.videoType !== 'desktop',
|
_showLocalVideoFlipButton: !disableLocalVideoFlip && videoTrack?.videoType !== 'desktop',
|
||||||
|
_showHideSelfViewButton: showHideSelfViewButton,
|
||||||
_overflowDrawer: overflowDrawer,
|
_overflowDrawer: overflowDrawer,
|
||||||
_localParticipantId: localParticipant.id,
|
_localParticipantId: localParticipant.id,
|
||||||
_showConnectionInfo: showConnectionInfo
|
_showConnectionInfo: showConnectionInfo
|
||||||
|
|
Loading…
Reference in New Issue