2018-10-30 05:02:23 +00:00
|
|
|
/* @flow */
|
|
|
|
|
2017-06-29 03:35:43 +00:00
|
|
|
import React, { Component } from 'react';
|
|
|
|
import { connect } from 'react-redux';
|
2019-03-19 15:42:25 +00:00
|
|
|
import type { Dispatch } from 'redux';
|
2017-06-29 03:35:43 +00:00
|
|
|
|
2017-12-05 03:27:17 +00:00
|
|
|
import { appendSuffix } from '../functions';
|
|
|
|
|
2017-06-29 03:35:43 +00:00
|
|
|
import { translate } from '../../base/i18n';
|
2019-01-13 19:33:28 +00:00
|
|
|
import {
|
|
|
|
getParticipantDisplayName
|
|
|
|
} from '../../base/participants';
|
|
|
|
import { updateSettings } from '../../base/settings';
|
2017-06-29 03:35:43 +00:00
|
|
|
|
|
|
|
/**
|
2018-10-30 05:02:23 +00:00
|
|
|
* The type of the React {@code Component} props of {@link DisplayName}.
|
2017-06-29 03:35:43 +00:00
|
|
|
*/
|
2018-10-30 05:02:23 +00:00
|
|
|
type Props = {
|
|
|
|
|
2019-01-13 19:33:28 +00:00
|
|
|
/**
|
|
|
|
* The participant's current display name.
|
|
|
|
*/
|
|
|
|
_displayName: string,
|
|
|
|
|
2017-06-29 03:35:43 +00:00
|
|
|
/**
|
2018-10-30 05:02:23 +00:00
|
|
|
* Whether or not the display name should be editable on click.
|
2017-06-29 03:35:43 +00:00
|
|
|
*/
|
2018-10-30 05:02:23 +00:00
|
|
|
allowEditing: boolean,
|
2017-06-29 03:35:43 +00:00
|
|
|
|
2018-10-30 05:02:23 +00:00
|
|
|
/**
|
|
|
|
* Invoked to update the participant's display name.
|
|
|
|
*/
|
2019-03-19 15:42:25 +00:00
|
|
|
dispatch: Dispatch<any>,
|
2017-06-29 03:35:43 +00:00
|
|
|
|
2018-10-30 05:02:23 +00:00
|
|
|
/**
|
|
|
|
* A string to append to the displayName, if provided.
|
|
|
|
*/
|
|
|
|
displayNameSuffix: string,
|
2017-06-29 03:35:43 +00:00
|
|
|
|
2018-10-30 05:02:23 +00:00
|
|
|
/**
|
|
|
|
* The ID attribute to add to the component. Useful for global querying for
|
|
|
|
* the component by legacy components and torture tests.
|
|
|
|
*/
|
|
|
|
elementID: string,
|
2017-06-29 03:35:43 +00:00
|
|
|
|
2018-10-30 05:02:23 +00:00
|
|
|
/**
|
|
|
|
* The ID of the participant whose name is being displayed.
|
|
|
|
*/
|
|
|
|
participantID: string,
|
2017-06-29 03:35:43 +00:00
|
|
|
|
2018-10-30 05:02:23 +00:00
|
|
|
/**
|
|
|
|
* Invoked to obtain translated strings.
|
|
|
|
*/
|
|
|
|
t: Function
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The type of the React {@code Component} state of {@link DisplayName}.
|
|
|
|
*/
|
|
|
|
type State = {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The current value of the display name in the edit field.
|
|
|
|
*/
|
|
|
|
editDisplayNameValue: string,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Whether or not the component should be displaying an editable input.
|
|
|
|
*/
|
|
|
|
isEditing: boolean
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* React {@code Component} for displaying and editing a participant's name.
|
|
|
|
*
|
|
|
|
* @extends Component
|
|
|
|
*/
|
|
|
|
class DisplayName extends Component<Props, State> {
|
|
|
|
_nameInput: ?HTMLInputElement;
|
2017-06-29 03:35:43 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Initializes a new {@code DisplayName} instance.
|
|
|
|
*
|
|
|
|
* @param {Object} props - The read-only properties with which the new
|
|
|
|
* instance is to be initialized.
|
|
|
|
*/
|
2018-10-30 05:02:23 +00:00
|
|
|
constructor(props: Props) {
|
2017-06-29 03:35:43 +00:00
|
|
|
super(props);
|
|
|
|
|
|
|
|
this.state = {
|
|
|
|
editDisplayNameValue: '',
|
|
|
|
isEditing: false
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The internal reference to the HTML element backing the React
|
|
|
|
* {@code Component} input with id {@code editDisplayName}. It is
|
|
|
|
* necessary for automatically selecting the display name input field
|
|
|
|
* when starting to edit the display name.
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
* @type {HTMLInputElement}
|
|
|
|
*/
|
|
|
|
this._nameInput = null;
|
|
|
|
|
|
|
|
// Bind event handlers so they are only bound once for every instance.
|
|
|
|
this._onChange = this._onChange.bind(this);
|
|
|
|
this._onKeyDown = this._onKeyDown.bind(this);
|
|
|
|
this._onStartEditing = this._onStartEditing.bind(this);
|
|
|
|
this._onSubmit = this._onSubmit.bind(this);
|
|
|
|
this._setNameInputRef = this._setNameInputRef.bind(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Automatically selects the input field's value after starting to edit the
|
|
|
|
* display name.
|
|
|
|
*
|
|
|
|
* @inheritdoc
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
|
|
|
componentDidUpdate(previousProps, previousState) {
|
2018-10-30 05:02:23 +00:00
|
|
|
if (!previousState.isEditing
|
|
|
|
&& this.state.isEditing
|
|
|
|
&& this._nameInput) {
|
2017-06-29 03:35:43 +00:00
|
|
|
this._nameInput.select();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Implements React's {@link Component#render()}.
|
|
|
|
*
|
|
|
|
* @inheritdoc
|
|
|
|
* @returns {ReactElement}
|
|
|
|
*/
|
|
|
|
render() {
|
|
|
|
const {
|
2019-01-13 19:33:28 +00:00
|
|
|
_displayName,
|
2017-06-29 03:35:43 +00:00
|
|
|
allowEditing,
|
|
|
|
displayNameSuffix,
|
|
|
|
elementID,
|
|
|
|
t
|
|
|
|
} = this.props;
|
|
|
|
|
|
|
|
if (allowEditing && this.state.isEditing) {
|
|
|
|
return (
|
|
|
|
<input
|
|
|
|
autoFocus = { true }
|
|
|
|
className = 'editdisplayname'
|
|
|
|
id = 'editDisplayName'
|
|
|
|
onBlur = { this._onSubmit }
|
|
|
|
onChange = { this._onChange }
|
|
|
|
onKeyDown = { this._onKeyDown }
|
|
|
|
placeholder = { t('defaultNickname') }
|
|
|
|
ref = { this._setNameInputRef }
|
2017-11-08 23:50:25 +00:00
|
|
|
spellCheck = { 'false' }
|
2017-06-29 03:35:43 +00:00
|
|
|
type = 'text'
|
|
|
|
value = { this.state.editDisplayNameValue } />
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<span
|
|
|
|
className = 'displayname'
|
|
|
|
id = { elementID }
|
|
|
|
onClick = { this._onStartEditing }>
|
2019-01-13 19:33:28 +00:00
|
|
|
{ appendSuffix(_displayName, displayNameSuffix) }
|
2017-06-29 03:35:43 +00:00
|
|
|
</span>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2018-10-30 05:02:23 +00:00
|
|
|
_onChange: () => void;
|
|
|
|
|
2017-06-29 03:35:43 +00:00
|
|
|
/**
|
|
|
|
* Updates the internal state of the display name entered into the edit
|
|
|
|
* field.
|
|
|
|
*
|
|
|
|
* @param {Object} event - DOM Event for value change.
|
|
|
|
* @private
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
|
|
|
_onChange(event) {
|
|
|
|
this.setState({
|
|
|
|
editDisplayNameValue: event.target.value
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-10-30 05:02:23 +00:00
|
|
|
_onKeyDown: () => void;
|
|
|
|
|
2017-06-29 03:35:43 +00:00
|
|
|
/**
|
|
|
|
* Submits the editted display name update if the enter key is pressed.
|
|
|
|
*
|
|
|
|
* @param {Event} event - Key down event object.
|
|
|
|
* @private
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
|
|
|
_onKeyDown(event) {
|
|
|
|
if (event.key === 'Enter') {
|
|
|
|
this._onSubmit();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-30 05:02:23 +00:00
|
|
|
_onStartEditing: () => void;
|
|
|
|
|
2017-06-29 03:35:43 +00:00
|
|
|
/**
|
|
|
|
* Updates the component to display an editable input field and sets the
|
|
|
|
* initial value to the current display name.
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
|
|
|
_onStartEditing() {
|
|
|
|
if (this.props.allowEditing) {
|
|
|
|
this.setState({
|
|
|
|
isEditing: true,
|
2019-01-13 19:33:28 +00:00
|
|
|
editDisplayNameValue: this.props._displayName || ''
|
2017-06-29 03:35:43 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-30 05:02:23 +00:00
|
|
|
_onSubmit: () => void;
|
|
|
|
|
2017-06-29 03:35:43 +00:00
|
|
|
/**
|
|
|
|
* Dispatches an action to update the display name if any change has
|
|
|
|
* occurred after editing. Clears any temporary state used to keep track
|
|
|
|
* of pending display name changes and exits editing mode.
|
|
|
|
*
|
|
|
|
* @param {Event} event - Key down event object.
|
|
|
|
* @private
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
|
|
|
_onSubmit() {
|
|
|
|
const { editDisplayNameValue } = this.state;
|
2019-01-13 19:33:28 +00:00
|
|
|
const { dispatch } = this.props;
|
2017-06-29 03:35:43 +00:00
|
|
|
|
2019-01-13 19:33:28 +00:00
|
|
|
// Store display name in settings
|
|
|
|
dispatch(updateSettings({
|
|
|
|
displayName: editDisplayNameValue
|
|
|
|
}));
|
2017-06-29 03:35:43 +00:00
|
|
|
|
|
|
|
this.setState({
|
|
|
|
isEditing: false,
|
|
|
|
editDisplayNameValue: ''
|
|
|
|
});
|
|
|
|
|
|
|
|
this._nameInput = null;
|
|
|
|
}
|
|
|
|
|
2018-10-30 05:02:23 +00:00
|
|
|
_setNameInputRef: (HTMLInputElement | null) => void;
|
|
|
|
|
2017-06-29 03:35:43 +00:00
|
|
|
/**
|
|
|
|
* Sets the internal reference to the HTML element backing the React
|
|
|
|
* {@code Component} input with id {@code editDisplayName}.
|
|
|
|
*
|
|
|
|
* @param {HTMLInputElement} element - The DOM/HTML element for this
|
|
|
|
* {@code Component}'s input.
|
|
|
|
* @private
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
|
|
|
_setNameInputRef(element) {
|
|
|
|
this._nameInput = element;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-13 19:33:28 +00:00
|
|
|
/**
|
|
|
|
* Maps (parts of) the redux state to the props of this component.
|
|
|
|
*
|
|
|
|
* @param {Object} state - The redux store/state.
|
|
|
|
* @param {Props} ownProps - The own props of the component.
|
|
|
|
* @private
|
|
|
|
* @returns {{
|
|
|
|
* _displayName: string
|
|
|
|
* }}
|
|
|
|
*/
|
|
|
|
function _mapStateToProps(state, ownProps) {
|
|
|
|
const { participantID } = ownProps;
|
|
|
|
|
|
|
|
return {
|
|
|
|
_displayName: getParticipantDisplayName(
|
|
|
|
state, participantID)
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
export default translate(connect(_mapStateToProps)(DisplayName));
|