You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ProfileTab.tsx 8.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. import { Theme } from '@mui/material';
  2. import { withStyles } from '@mui/styles';
  3. import React from 'react';
  4. import { WithTranslation } from 'react-i18next';
  5. // @ts-expect-error
  6. import UIEvents from '../../../../../service/UI/UIEvents';
  7. import { createProfilePanelButtonEvent } from '../../../analytics/AnalyticsEvents';
  8. import { sendAnalytics } from '../../../analytics/functions';
  9. import Avatar from '../../../base/avatar/components/Avatar';
  10. import AbstractDialogTab, {
  11. IProps as AbstractDialogTabProps } from '../../../base/dialog/components/web/AbstractDialogTab';
  12. import { translate } from '../../../base/i18n/functions';
  13. import { withPixelLineHeight } from '../../../base/styles/functions.web';
  14. import Button from '../../../base/ui/components/web/Button';
  15. import Input from '../../../base/ui/components/web/Input';
  16. import Select from '../../../base/ui/components/web/Select';
  17. import { openLogoutDialog } from '../../actions';
  18. /**
  19. * The type of the React {@code Component} props of {@link ProfileTab}.
  20. */
  21. export interface IProps extends AbstractDialogTabProps, WithTranslation {
  22. /**
  23. * Whether or not server-side authentication is available.
  24. */
  25. authEnabled: boolean;
  26. /**
  27. * The name of the currently (server-side) authenticated user.
  28. */
  29. authLogin: string;
  30. /**
  31. * CSS classes object.
  32. */
  33. classes: any;
  34. /**
  35. * The currently selected language to display in the language select
  36. * dropdown.
  37. */
  38. currentLanguage: string;
  39. /**
  40. * The display name to display for the local participant.
  41. */
  42. displayName: string;
  43. /**
  44. * The email to display for the local participant.
  45. */
  46. email: string;
  47. /**
  48. * Whether to hide the email input in the profile settings.
  49. */
  50. hideEmailInSettings?: boolean;
  51. /**
  52. * The id of the local participant.
  53. */
  54. id: string;
  55. /**
  56. * All available languages to display in the language select dropdown.
  57. */
  58. languages: Array<string>;
  59. /**
  60. * If the display name is read only.
  61. */
  62. readOnlyName: boolean;
  63. /**
  64. * Whether or not to display the language select dropdown.
  65. */
  66. showLanguageSettings: boolean;
  67. }
  68. const styles = (theme: Theme) => {
  69. return {
  70. container: {
  71. display: 'flex',
  72. flexDirection: 'column' as const,
  73. width: '100%',
  74. padding: '0 2px'
  75. },
  76. avatarContainer: {
  77. display: 'flex',
  78. width: '100%',
  79. justifyContent: 'center',
  80. marginBottom: theme.spacing(4)
  81. },
  82. bottomMargin: {
  83. marginBottom: theme.spacing(4)
  84. },
  85. label: {
  86. color: `${theme.palette.text01} !important`,
  87. ...withPixelLineHeight(theme.typography.bodyShortRegular),
  88. marginBottom: theme.spacing(2)
  89. },
  90. name: {
  91. marginBottom: theme.spacing(1)
  92. }
  93. };
  94. };
  95. /**
  96. * React {@code Component} for modifying the local user's profile.
  97. *
  98. * @augments Component
  99. */
  100. class ProfileTab extends AbstractDialogTab<IProps, any> {
  101. static defaultProps = {
  102. displayName: '',
  103. email: ''
  104. };
  105. /**
  106. * Initializes a new {@code ConnectedSettingsDialog} instance.
  107. *
  108. * @param {IProps} props - The React {@code Component} props to initialize
  109. * the new {@code ConnectedSettingsDialog} instance with.
  110. */
  111. constructor(props: IProps) {
  112. super(props);
  113. // Bind event handlers so they are only bound once for every instance.
  114. this._onAuthToggle = this._onAuthToggle.bind(this);
  115. this._onDisplayNameChange = this._onDisplayNameChange.bind(this);
  116. this._onEmailChange = this._onEmailChange.bind(this);
  117. this._onLanguageItemSelect = this._onLanguageItemSelect.bind(this);
  118. }
  119. /**
  120. * Changes display name of the user.
  121. *
  122. * @param {string} value - The key event to handle.
  123. *
  124. * @returns {void}
  125. */
  126. _onDisplayNameChange(value: string) {
  127. super._onChange({ displayName: value });
  128. }
  129. /**
  130. * Changes email of the user.
  131. *
  132. * @param {string} value - The key event to handle.
  133. *
  134. * @returns {void}
  135. */
  136. _onEmailChange(value: string) {
  137. super._onChange({ email: value });
  138. }
  139. /**
  140. * Callback invoked to select a language from select dropdown.
  141. *
  142. * @param {Object} e - The key event to handle.
  143. *
  144. * @returns {void}
  145. */
  146. _onLanguageItemSelect(e: React.ChangeEvent<HTMLSelectElement>) {
  147. const language = e.target.value;
  148. super._onChange({ currentLanguage: language });
  149. }
  150. /**
  151. * Returns the menu item for changing displayed language.
  152. *
  153. * @private
  154. * @returns {ReactElement}
  155. */
  156. _renderLanguageSelect() {
  157. const {
  158. classes,
  159. currentLanguage,
  160. languages,
  161. t
  162. } = this.props;
  163. const languageItems
  164. = languages.map((language: string) => {
  165. return {
  166. value: language,
  167. label: t(`languages:${language}`)
  168. };
  169. });
  170. return (
  171. <Select
  172. className = { classes.bottomMargin }
  173. label = { t('settings.language') }
  174. onChange = { this._onLanguageItemSelect }
  175. options = { languageItems }
  176. value = { currentLanguage } />
  177. );
  178. }
  179. /**
  180. * Implements React's {@link Component#render()}.
  181. *
  182. * @inheritdoc
  183. * @returns {ReactElement}
  184. */
  185. render() {
  186. const {
  187. authEnabled,
  188. classes,
  189. displayName,
  190. email,
  191. hideEmailInSettings,
  192. id,
  193. readOnlyName,
  194. showLanguageSettings,
  195. t
  196. } = this.props;
  197. return (
  198. <div className = { classes.container } >
  199. <div className = { classes.avatarContainer }>
  200. <Avatar
  201. participantId = { id }
  202. size = { 60 } />
  203. </div>
  204. <Input
  205. className = { classes.bottomMargin }
  206. disabled = { readOnlyName }
  207. id = 'setDisplayName'
  208. label = { t('profile.setDisplayNameLabel') }
  209. name = 'name'
  210. onChange = { this._onDisplayNameChange }
  211. placeholder = { t('settings.name') }
  212. type = 'text'
  213. value = { displayName } />
  214. {!hideEmailInSettings && <div className = 'profile-edit-field'>
  215. <Input
  216. className = { classes.bottomMargin }
  217. id = 'setEmail'
  218. label = { t('profile.setEmailLabel') }
  219. name = 'email'
  220. onChange = { this._onEmailChange }
  221. placeholder = { t('profile.setEmailInput') }
  222. type = 'text'
  223. value = { email } />
  224. </div>}
  225. {showLanguageSettings && this._renderLanguageSelect()}
  226. { authEnabled && this._renderAuth() }
  227. </div>
  228. );
  229. }
  230. /**
  231. * Shows the dialog for logging in or out of a server and closes this
  232. * dialog.
  233. *
  234. * @private
  235. * @returns {void}
  236. */
  237. _onAuthToggle() {
  238. if (this.props.authLogin) {
  239. sendAnalytics(createProfilePanelButtonEvent('logout.button'));
  240. APP.store.dispatch(openLogoutDialog(
  241. () => APP.UI.emitEvent(UIEvents.LOGOUT)
  242. ));
  243. } else {
  244. sendAnalytics(createProfilePanelButtonEvent('login.button'));
  245. APP.UI.emitEvent(UIEvents.AUTH_CLICKED);
  246. }
  247. }
  248. /**
  249. * Returns a React Element for interacting with server-side authentication.
  250. *
  251. * @private
  252. * @returns {ReactElement}
  253. */
  254. _renderAuth() {
  255. const {
  256. authLogin,
  257. classes,
  258. t
  259. } = this.props;
  260. return (
  261. <div>
  262. <h2 className = { classes.label }>
  263. { t('toolbar.authenticate') }
  264. </h2>
  265. { authLogin
  266. && <div className = { classes.name }>
  267. { t('settings.loggedIn', { name: authLogin }) }
  268. </div> }
  269. <Button
  270. accessibilityLabel = { authLogin ? t('toolbar.logout') : t('toolbar.login') }
  271. id = 'login_button'
  272. label = { authLogin ? t('toolbar.logout') : t('toolbar.login') }
  273. onClick = { this._onAuthToggle } />
  274. </div>
  275. );
  276. }
  277. }
  278. export default withStyles(styles)(translate(ProfileTab));