您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

DisplayName.web.js 7.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. /* @flow */
  2. import React, { Component } from 'react';
  3. import { connect } from 'react-redux';
  4. import { appendSuffix } from '../functions';
  5. import { translate } from '../../base/i18n';
  6. import {
  7. getParticipantDisplayName
  8. } from '../../base/participants';
  9. import { updateSettings } from '../../base/settings';
  10. /**
  11. * The type of the React {@code Component} props of {@link DisplayName}.
  12. */
  13. type Props = {
  14. /**
  15. * The participant's current display name.
  16. */
  17. _displayName: string,
  18. /**
  19. * Whether or not the display name should be editable on click.
  20. */
  21. allowEditing: boolean,
  22. /**
  23. * Invoked to update the participant's display name.
  24. */
  25. dispatch: Dispatch<*>,
  26. /**
  27. * A string to append to the displayName, if provided.
  28. */
  29. displayNameSuffix: string,
  30. /**
  31. * The ID attribute to add to the component. Useful for global querying for
  32. * the component by legacy components and torture tests.
  33. */
  34. elementID: string,
  35. /**
  36. * The ID of the participant whose name is being displayed.
  37. */
  38. participantID: string,
  39. /**
  40. * Invoked to obtain translated strings.
  41. */
  42. t: Function
  43. };
  44. /**
  45. * The type of the React {@code Component} state of {@link DisplayName}.
  46. */
  47. type State = {
  48. /**
  49. * The current value of the display name in the edit field.
  50. */
  51. editDisplayNameValue: string,
  52. /**
  53. * Whether or not the component should be displaying an editable input.
  54. */
  55. isEditing: boolean
  56. };
  57. /**
  58. * React {@code Component} for displaying and editing a participant's name.
  59. *
  60. * @extends Component
  61. */
  62. class DisplayName extends Component<Props, State> {
  63. _nameInput: ?HTMLInputElement;
  64. /**
  65. * Initializes a new {@code DisplayName} instance.
  66. *
  67. * @param {Object} props - The read-only properties with which the new
  68. * instance is to be initialized.
  69. */
  70. constructor(props: Props) {
  71. super(props);
  72. this.state = {
  73. editDisplayNameValue: '',
  74. isEditing: false
  75. };
  76. /**
  77. * The internal reference to the HTML element backing the React
  78. * {@code Component} input with id {@code editDisplayName}. It is
  79. * necessary for automatically selecting the display name input field
  80. * when starting to edit the display name.
  81. *
  82. * @private
  83. * @type {HTMLInputElement}
  84. */
  85. this._nameInput = null;
  86. // Bind event handlers so they are only bound once for every instance.
  87. this._onChange = this._onChange.bind(this);
  88. this._onKeyDown = this._onKeyDown.bind(this);
  89. this._onStartEditing = this._onStartEditing.bind(this);
  90. this._onSubmit = this._onSubmit.bind(this);
  91. this._setNameInputRef = this._setNameInputRef.bind(this);
  92. }
  93. /**
  94. * Automatically selects the input field's value after starting to edit the
  95. * display name.
  96. *
  97. * @inheritdoc
  98. * @returns {void}
  99. */
  100. componentDidUpdate(previousProps, previousState) {
  101. if (!previousState.isEditing
  102. && this.state.isEditing
  103. && this._nameInput) {
  104. this._nameInput.select();
  105. }
  106. }
  107. /**
  108. * Implements React's {@link Component#render()}.
  109. *
  110. * @inheritdoc
  111. * @returns {ReactElement}
  112. */
  113. render() {
  114. const {
  115. _displayName,
  116. allowEditing,
  117. displayNameSuffix,
  118. elementID,
  119. t
  120. } = this.props;
  121. if (allowEditing && this.state.isEditing) {
  122. return (
  123. <input
  124. autoFocus = { true }
  125. className = 'editdisplayname'
  126. id = 'editDisplayName'
  127. onBlur = { this._onSubmit }
  128. onChange = { this._onChange }
  129. onKeyDown = { this._onKeyDown }
  130. placeholder = { t('defaultNickname') }
  131. ref = { this._setNameInputRef }
  132. spellCheck = { 'false' }
  133. type = 'text'
  134. value = { this.state.editDisplayNameValue } />
  135. );
  136. }
  137. return (
  138. <span
  139. className = 'displayname'
  140. id = { elementID }
  141. onClick = { this._onStartEditing }>
  142. { appendSuffix(_displayName, displayNameSuffix) }
  143. </span>
  144. );
  145. }
  146. _onChange: () => void;
  147. /**
  148. * Updates the internal state of the display name entered into the edit
  149. * field.
  150. *
  151. * @param {Object} event - DOM Event for value change.
  152. * @private
  153. * @returns {void}
  154. */
  155. _onChange(event) {
  156. this.setState({
  157. editDisplayNameValue: event.target.value
  158. });
  159. }
  160. _onKeyDown: () => void;
  161. /**
  162. * Submits the editted display name update if the enter key is pressed.
  163. *
  164. * @param {Event} event - Key down event object.
  165. * @private
  166. * @returns {void}
  167. */
  168. _onKeyDown(event) {
  169. if (event.key === 'Enter') {
  170. this._onSubmit();
  171. }
  172. }
  173. _onStartEditing: () => void;
  174. /**
  175. * Updates the component to display an editable input field and sets the
  176. * initial value to the current display name.
  177. *
  178. * @private
  179. * @returns {void}
  180. */
  181. _onStartEditing() {
  182. if (this.props.allowEditing) {
  183. this.setState({
  184. isEditing: true,
  185. editDisplayNameValue: this.props._displayName || ''
  186. });
  187. }
  188. }
  189. _onSubmit: () => void;
  190. /**
  191. * Dispatches an action to update the display name if any change has
  192. * occurred after editing. Clears any temporary state used to keep track
  193. * of pending display name changes and exits editing mode.
  194. *
  195. * @param {Event} event - Key down event object.
  196. * @private
  197. * @returns {void}
  198. */
  199. _onSubmit() {
  200. const { editDisplayNameValue } = this.state;
  201. const { dispatch } = this.props;
  202. // Store display name in settings
  203. dispatch(updateSettings({
  204. displayName: editDisplayNameValue
  205. }));
  206. this.setState({
  207. isEditing: false,
  208. editDisplayNameValue: ''
  209. });
  210. this._nameInput = null;
  211. }
  212. _setNameInputRef: (HTMLInputElement | null) => void;
  213. /**
  214. * Sets the internal reference to the HTML element backing the React
  215. * {@code Component} input with id {@code editDisplayName}.
  216. *
  217. * @param {HTMLInputElement} element - The DOM/HTML element for this
  218. * {@code Component}'s input.
  219. * @private
  220. * @returns {void}
  221. */
  222. _setNameInputRef(element) {
  223. this._nameInput = element;
  224. }
  225. }
  226. /**
  227. * Maps (parts of) the redux state to the props of this component.
  228. *
  229. * @param {Object} state - The redux store/state.
  230. * @param {Props} ownProps - The own props of the component.
  231. * @private
  232. * @returns {{
  233. * _displayName: string
  234. * }}
  235. */
  236. function _mapStateToProps(state, ownProps) {
  237. const { participantID } = ownProps;
  238. return {
  239. _displayName: getParticipantDisplayName(
  240. state, participantID)
  241. };
  242. }
  243. export default translate(connect(_mapStateToProps)(DisplayName));