feat(config): Add config option for making display name read only

This commit is contained in:
Vlad Piersec 2021-09-06 13:01:13 +03:00 committed by vp8x8
parent 8fd7b10f06
commit fc6e8fd4b9
8 changed files with 51 additions and 10 deletions

View File

@ -476,6 +476,10 @@ var config = {
// When 'true', it shows an intermediate page before joining, where the user can configure their devices. // When 'true', it shows an intermediate page before joining, where the user can configure their devices.
// prejoinPageEnabled: false, // prejoinPageEnabled: false,
// When 'true', the user cannot edit the display name.
// (Mainly useful when used in conjuction with the JWT so the JWT name becomes read only.)
// readOnlyName: false,
// If etherpad integration is enabled, setting this to true will // If etherpad integration is enabled, setting this to true will
// automatically open the etherpad when a participant joins. This // automatically open the etherpad when a participant joins. This
// does not affect the mobile app since opening an etherpad // does not affect the mobile app since opening an etherpad

View File

@ -175,6 +175,7 @@ export default [
'requireDisplayName', 'requireDisplayName',
'remoteVideoMenu', 'remoteVideoMenu',
'roomPasswordNumberOfDigits', 'roomPasswordNumberOfDigits',
'readOnlyName',
'replaceParticipant', 'replaceParticipant',
'resolution', 'resolution',
'startAudioMuted', 'startAudioMuted',

View File

@ -152,6 +152,18 @@ export function getWhitelistedJSON(configName: string, configJSON: Object): Obje
return configJSON; return configJSON;
} }
/**
* Selector for determining if the display name is read only.
*
* @param {Object} state - The state of the app.
* @returns {boolean}
*/
export function isNameReadOnly(state: Object): boolean {
return state['features/base/config'].disableProfile
|| state['features/base/config'].readOnlyName;
}
/** /**
* Restores a Jitsi Meet config.js from {@code localStorage} if it was * Restores a Jitsi Meet config.js from {@code localStorage} if it was
* previously downloaded from a specific {@code baseURL} and stored with * previously downloaded from a specific {@code baseURL} and stored with

View File

@ -36,6 +36,11 @@ type Props = {
*/ */
placeHolder: string, placeHolder: string,
/**
* Whether the input is read only or not.
*/
readOnly?: boolean,
/** /**
* The field type (e.g. text, password...etc). * The field type (e.g. text, password...etc).
*/ */
@ -126,6 +131,7 @@ export default class InputField extends PureComponent<Props, State> {
onFocus = { this._onFocus } onFocus = { this._onFocus }
onKeyDown = { this._onKeyDown } onKeyDown = { this._onKeyDown }
placeholder = { this.props.placeHolder } placeholder = { this.props.placeHolder }
readOnly = { this.props.readOnly }
type = { this.props.type } type = { this.props.type }
value = { this.state.value } /> value = { this.state.value } />
); );

View File

@ -5,6 +5,7 @@ import React, { Component } from 'react';
import { createScreenSharingIssueEvent, sendAnalytics } from '../../../analytics'; import { createScreenSharingIssueEvent, sendAnalytics } from '../../../analytics';
import { AudioLevelIndicator } from '../../../audio-level-indicator'; import { AudioLevelIndicator } from '../../../audio-level-indicator';
import { Avatar } from '../../../base/avatar'; import { Avatar } from '../../../base/avatar';
import { isNameReadOnly } from '../../../base/config';
import { isMobileBrowser } from '../../../base/environment/utils'; import { isMobileBrowser } from '../../../base/environment/utils';
import JitsiMeetJS from '../../../base/lib-jitsi-meet/_'; import JitsiMeetJS from '../../../base/lib-jitsi-meet/_';
import { MEDIA_TYPE, VideoTrack } from '../../../base/media'; import { MEDIA_TYPE, VideoTrack } from '../../../base/media';
@ -73,6 +74,11 @@ export type State = {|
*/ */
export type Props = {| export type Props = {|
/**
* If the display name is editable or not.
*/
_allowEditing: boolean,
/** /**
* The audio track related to the participant. * The audio track related to the participant.
*/ */
@ -103,11 +109,6 @@ export type Props = {|
*/ */
_disableLocalVideoFlip: boolean, _disableLocalVideoFlip: boolean,
/**
* Indicates whether the profile functionality is disabled.
*/
_disableProfile: boolean,
/** /**
* The display mode of the thumbnail. * The display mode of the thumbnail.
*/ */
@ -763,13 +764,13 @@ class Thumbnail extends Component<Props, State> {
*/ */
_renderLocalParticipant() { _renderLocalParticipant() {
const { const {
_allowEditing,
_defaultLocalDisplayName, _defaultLocalDisplayName,
_disableLocalVideoFlip, _disableLocalVideoFlip,
_isMobile, _isMobile,
_isMobilePortrait, _isMobilePortrait,
_isScreenSharing, _isScreenSharing,
_localFlipX, _localFlipX,
_disableProfile,
_participant, _participant,
_videoTrack _videoTrack
} = this.props; } = this.props;
@ -820,7 +821,7 @@ class Thumbnail extends Component<Props, State> {
className = 'displayNameContainer' className = 'displayNameContainer'
onClick = { onClick }> onClick = { onClick }>
<DisplayName <DisplayName
allowEditing = { !_disableProfile } allowEditing = { _allowEditing }
displayNameSuffix = { _defaultLocalDisplayName } displayNameSuffix = { _defaultLocalDisplayName }
elementID = 'localDisplayName' elementID = 'localDisplayName'
participantID = { id } /> participantID = { id } />
@ -1053,7 +1054,6 @@ function _mapStateToProps(state, ownProps): Object {
const { const {
startSilent, startSilent,
disableLocalVideoFlip, disableLocalVideoFlip,
disableProfile,
iAmRecorder, iAmRecorder,
iAmSipGateway iAmSipGateway
} = state['features/base/config']; } = state['features/base/config'];
@ -1102,6 +1102,7 @@ function _mapStateToProps(state, ownProps): Object {
} }
return { return {
_allowEditing: !isNameReadOnly(state),
_audioTrack, _audioTrack,
_connectionIndicatorAutoHideEnabled: _connectionIndicatorAutoHideEnabled:
Boolean(state['features/base/config'].connectionIndicators?.autoHide ?? true), Boolean(state['features/base/config'].connectionIndicators?.autoHide ?? true),
@ -1110,7 +1111,6 @@ function _mapStateToProps(state, ownProps): Object {
_currentLayout, _currentLayout,
_defaultLocalDisplayName: interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME, _defaultLocalDisplayName: interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME,
_disableLocalVideoFlip: Boolean(disableLocalVideoFlip), _disableLocalVideoFlip: Boolean(disableLocalVideoFlip),
_disableProfile: disableProfile,
_isHidden: isLocal && iAmRecorder && !iAmSipGateway, _isHidden: isLocal && iAmRecorder && !iAmSipGateway,
_isAudioOnly: Boolean(state['features/base/audio-only'].enabled), _isAudioOnly: Boolean(state['features/base/audio-only'].enabled),
_isCurrentlyOnLargeVideo: state['features/large-video']?.participantId === id, _isCurrentlyOnLargeVideo: state['features/large-video']?.participantId === id,

View File

@ -4,6 +4,7 @@ import InlineDialog from '@atlaskit/inline-dialog';
import React, { Component } from 'react'; import React, { Component } from 'react';
import { getRoomName } from '../../base/conference'; import { getRoomName } from '../../base/conference';
import { isNameReadOnly } from '../../base/config';
import { translate } from '../../base/i18n'; import { translate } from '../../base/i18n';
import { Icon, IconArrowDown, IconArrowUp, IconPhone, IconVolumeOff } from '../../base/icons'; import { Icon, IconArrowDown, IconArrowUp, IconPhone, IconVolumeOff } from '../../base/icons';
import { isVideoMutedByUser } from '../../base/media'; import { isVideoMutedByUser } from '../../base/media';
@ -57,6 +58,11 @@ type Props = {
*/ */
updateSettings: Function, updateSettings: Function,
/**
* Whether the name input should be read only or not.
*/
readOnlyName: boolean,
/** /**
* The name of the meeting that is about to be joined. * The name of the meeting that is about to be joined.
*/ */
@ -283,6 +289,7 @@ class Prejoin extends Component<Props, State> {
joinConference, joinConference,
joinConferenceWithoutAudio, joinConferenceWithoutAudio,
name, name,
readOnlyName,
showCameraPreview, showCameraPreview,
showDialog, showDialog,
t, t,
@ -310,6 +317,7 @@ class Prejoin extends Component<Props, State> {
onChange = { _setName } onChange = { _setName }
onSubmit = { joinConference } onSubmit = { joinConference }
placeHolder = { t('dialog.enterDisplayName') } placeHolder = { t('dialog.enterDisplayName') }
readOnly = { readOnlyName }
value = { name } /> value = { name } />
{showError && <div {showError && <div
@ -393,6 +401,7 @@ function mapStateToProps(state): Object {
showDialog: isJoinByPhoneDialogVisible(state), showDialog: isJoinByPhoneDialogVisible(state),
showErrorOnJoin, showErrorOnJoin,
hasJoinByPhoneButton: isJoinByPhoneButtonVisible(state), hasJoinByPhoneButton: isJoinByPhoneButtonVisible(state),
readOnlyName: isNameReadOnly(state),
showCameraPreview: !isVideoMutedByUser(state), showCameraPreview: !isVideoMutedByUser(state),
videoTrack: getLocalJitsiVideoTrack(state) videoTrack: getLocalJitsiVideoTrack(state)
}; };

View File

@ -42,6 +42,11 @@ export type Props = {
*/ */
email: string, email: string,
/**
* If the display name is read only.
*/
readOnlyName: boolean,
/** /**
* Invoked to obtain translated strings. * Invoked to obtain translated strings.
*/ */
@ -111,6 +116,7 @@ class ProfileTab extends AbstractDialogTab<Props> {
authEnabled, authEnabled,
displayName, displayName,
email, email,
readOnlyName,
t t
} = this.props; } = this.props;
@ -122,6 +128,7 @@ class ProfileTab extends AbstractDialogTab<Props> {
autoComplete = 'name' autoComplete = 'name'
compact = { true } compact = { true }
id = 'setDisplayName' id = 'setDisplayName'
isReadOnly = { readOnlyName }
label = { t('profile.setDisplayNameLabel') } label = { t('profile.setDisplayNameLabel') }
onChange = { this._onDisplayNameChange } onChange = { this._onDisplayNameChange }
placeholder = { t('settings.name') } placeholder = { t('settings.name') }

View File

@ -1,5 +1,6 @@
// @flow // @flow
import { isNameReadOnly } from '../base/config';
import { SERVER_URL_CHANGE_ENABLED, getFeatureFlag } from '../base/flags'; import { SERVER_URL_CHANGE_ENABLED, getFeatureFlag } from '../base/flags';
import { i18next, DEFAULT_LANGUAGE, LANGUAGES } from '../base/i18n'; import { i18next, DEFAULT_LANGUAGE, LANGUAGES } from '../base/i18n';
import { createLocalTrack } from '../base/lib-jitsi-meet/functions'; import { createLocalTrack } from '../base/lib-jitsi-meet/functions';
@ -153,7 +154,8 @@ export function getProfileTabProps(stateful: Object | Function) {
authEnabled: Boolean(conference && authEnabled), authEnabled: Boolean(conference && authEnabled),
authLogin, authLogin,
displayName: localParticipant.name, displayName: localParticipant.name,
email: localParticipant.email email: localParticipant.email,
readOnlyName: isNameReadOnly(state)
}; };
} }