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.

LoginDialog.js 8.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. // @flow
  2. import { FieldTextStateless as TextField } from '@atlaskit/field-text';
  3. import React, { Component } from 'react';
  4. import type { Dispatch } from 'redux';
  5. import { connect } from '../../../../../connection';
  6. import { toJid } from '../../../base/connection/functions';
  7. import { Dialog } from '../../../base/dialog';
  8. import { translate, translateToHTML } from '../../../base/i18n';
  9. import { JitsiConnectionErrors } from '../../../base/lib-jitsi-meet';
  10. import { connect as reduxConnect } from '../../../base/redux';
  11. import {
  12. authenticateAndUpgradeRole,
  13. cancelLogin
  14. } from '../../actions.web';
  15. /**
  16. * The type of the React {@code Component} props of {@link LoginDialog}.
  17. */
  18. type Props = {
  19. /**
  20. * {@link JitsiConference} that needs authentication - will hold a valid
  21. * value in XMPP login + guest access mode.
  22. */
  23. _conference: Object,
  24. /**
  25. * The server hosts specified in the global config.
  26. */
  27. _configHosts: Object,
  28. /**
  29. * Indicates if the dialog should display "connecting" status message.
  30. */
  31. _connecting: boolean,
  32. /**
  33. * The error which occurred during login/authentication.
  34. */
  35. _error: Object,
  36. /**
  37. * The progress in the floating range between 0 and 1 of the authenticating
  38. * and upgrading the role of the local participant/user.
  39. */
  40. _progress: number,
  41. /**
  42. * Redux store dispatch method.
  43. */
  44. dispatch: Dispatch<any>,
  45. /**
  46. * Invoked when username and password are submitted.
  47. */
  48. onSuccess: Function,
  49. /**
  50. * Conference room name.
  51. */
  52. roomName: string,
  53. /**
  54. * Invoked to obtain translated strings.
  55. */
  56. t: Function
  57. }
  58. /**
  59. * The type of the React {@code Component} state of {@link LoginDialog}.
  60. */
  61. type State = {
  62. /**
  63. * The user entered password for the conference.
  64. */
  65. password: string,
  66. /**
  67. * The user entered local participant name.
  68. */
  69. username: string,
  70. /**
  71. * Authentication process starts before joining the conference room.
  72. */
  73. loginStarted: boolean
  74. }
  75. /**
  76. * Component that renders the login in conference dialog.
  77. *
  78. * @returns {React$Element<any>}
  79. */
  80. class LoginDialog extends Component<Props, State> {
  81. /**
  82. * Initializes a new {@code LoginDialog} instance.
  83. *
  84. * @inheritdoc
  85. */
  86. constructor(props: Props) {
  87. super(props);
  88. this.state = {
  89. username: '',
  90. password: '',
  91. loginStarted: false
  92. };
  93. this._onCancelLogin = this._onCancelLogin.bind(this);
  94. this._onLogin = this._onLogin.bind(this);
  95. this._onChange = this._onChange.bind(this);
  96. }
  97. _onCancelLogin: () => void;
  98. /**
  99. * Called when the cancel button is clicked.
  100. *
  101. * @private
  102. * @returns {void}
  103. */
  104. _onCancelLogin() {
  105. const { dispatch } = this.props;
  106. dispatch(cancelLogin());
  107. }
  108. _onLogin: () => void;
  109. /**
  110. * Notifies this LoginDialog that the login button (OK) has been pressed by
  111. * the user.
  112. *
  113. * @private
  114. * @returns {void}
  115. */
  116. _onLogin() {
  117. const {
  118. _conference: conference,
  119. _configHosts: configHosts,
  120. roomName,
  121. onSuccess,
  122. dispatch
  123. } = this.props;
  124. const { password, username } = this.state;
  125. const jid = toJid(username, configHosts);
  126. if (conference) {
  127. dispatch(authenticateAndUpgradeRole(jid, password, conference));
  128. } else {
  129. this.setState({
  130. loginStarted: true
  131. });
  132. connect(jid, password, roomName)
  133. .then(connection => {
  134. onSuccess && onSuccess(connection);
  135. })
  136. .catch(() => {
  137. this.setState({
  138. loginStarted: false
  139. });
  140. });
  141. }
  142. }
  143. _onChange: Object => void;
  144. /**
  145. * Callback for the onChange event of the field.
  146. *
  147. * @param {Object} evt - The static event.
  148. * @returns {void}
  149. */
  150. _onChange(evt: Object) {
  151. this.setState({
  152. [evt.target.name]: evt.target.value
  153. });
  154. }
  155. /**
  156. * Renders an optional message, if applicable.
  157. *
  158. * @returns {ReactElement}
  159. * @private
  160. */
  161. renderMessage() {
  162. const {
  163. _configHosts: configHosts,
  164. _connecting: connecting,
  165. _error: error,
  166. _progress: progress,
  167. t
  168. } = this.props;
  169. const { username, password } = this.state;
  170. const messageOptions = {};
  171. let messageKey;
  172. if (progress && progress < 1) {
  173. messageKey = t('connection.FETCH_SESSION_ID');
  174. } else if (error) {
  175. const { name } = error;
  176. if (name === JitsiConnectionErrors.PASSWORD_REQUIRED) {
  177. const { credentials } = error;
  178. if (credentials
  179. && credentials.jid === toJid(username, configHosts)
  180. && credentials.password === password) {
  181. messageKey = t('dialog.incorrectPassword');
  182. }
  183. } else if (name) {
  184. messageKey = t('dialog.connectErrorWithMsg');
  185. messageOptions.msg = `${name} ${error.message}`;
  186. }
  187. } else if (connecting) {
  188. messageKey = t('connection.CONNECTING');
  189. }
  190. if (messageKey) {
  191. return (
  192. <span>
  193. { translateToHTML(t, messageKey, messageOptions) }
  194. </span>
  195. );
  196. }
  197. return null;
  198. }
  199. /**
  200. * Implements {@Component#render}.
  201. *
  202. * @inheritdoc
  203. */
  204. render() {
  205. const {
  206. _connecting: connecting,
  207. t
  208. } = this.props;
  209. const { password, loginStarted, username } = this.state;
  210. return (
  211. <Dialog
  212. disableBlanketClickDismiss = { true }
  213. hideCloseIconButton = { true }
  214. okDisabled = {
  215. connecting
  216. || loginStarted
  217. || !password
  218. || !username
  219. }
  220. okKey = { t('dialog.login') }
  221. onCancel = { this._onCancelLogin }
  222. onSubmit = { this._onLogin }
  223. titleKey = { t('dialog.authenticationRequired') }
  224. width = { 'small' }>
  225. <TextField
  226. autoFocus = { true }
  227. className = 'input-control'
  228. compact = { false }
  229. label = { t('dialog.user') }
  230. name = 'username'
  231. onChange = { this._onChange }
  232. placeholder = { t('dialog.userIdentifier') }
  233. shouldFitContainer = { true }
  234. type = 'text'
  235. value = { username } />
  236. <TextField
  237. className = 'input-control'
  238. compact = { false }
  239. label = { t('dialog.userPassword') }
  240. name = 'password'
  241. onChange = { this._onChange }
  242. placeholder = { t('dialog.password') }
  243. shouldFitContainer = { true }
  244. type = 'password'
  245. value = { password } />
  246. { this.renderMessage() }
  247. </Dialog>
  248. );
  249. }
  250. }
  251. /**
  252. * Maps (parts of) the Redux state to the associated props for the
  253. * {@code LoginDialog} component.
  254. *
  255. * @param {Object} state - The Redux state.
  256. * @private
  257. * @returns {Props}
  258. */
  259. function mapStateToProps(state) {
  260. const {
  261. error: authenticateAndUpgradeRoleError,
  262. progress,
  263. thenableWithCancel
  264. } = state['features/authentication'];
  265. const { authRequired } = state['features/base/conference'];
  266. const { hosts: configHosts } = state['features/base/config'];
  267. const {
  268. connecting,
  269. error: connectionError
  270. } = state['features/base/connection'];
  271. return {
  272. _conference: authRequired,
  273. _configHosts: configHosts,
  274. _connecting: connecting || thenableWithCancel,
  275. _error: connectionError || authenticateAndUpgradeRoleError,
  276. _progress: progress
  277. };
  278. }
  279. export default translate(reduxConnect(mapStateToProps)(LoginDialog));