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

LoginDialog.js 8.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  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. const cancelButton = document.getElementById('modal-dialog-cancel-button');
  107. if (cancelButton) {
  108. cancelButton.onclick = () => {
  109. dispatch(cancelLogin());
  110. };
  111. }
  112. return false;
  113. }
  114. _onLogin: () => void;
  115. /**
  116. * Notifies this LoginDialog that the login button (OK) has been pressed by
  117. * the user.
  118. *
  119. * @private
  120. * @returns {void}
  121. */
  122. _onLogin() {
  123. const {
  124. _conference: conference,
  125. _configHosts: configHosts,
  126. roomName,
  127. onSuccess,
  128. dispatch
  129. } = this.props;
  130. const { password, username } = this.state;
  131. const jid = toJid(username, configHosts);
  132. if (conference) {
  133. dispatch(authenticateAndUpgradeRole(jid, password, conference));
  134. } else {
  135. this.setState({
  136. loginStarted: true
  137. });
  138. connect(jid, password, roomName)
  139. .then(connection => {
  140. onSuccess && onSuccess(connection);
  141. })
  142. .catch(() => {
  143. this.setState({
  144. loginStarted: false
  145. });
  146. });
  147. }
  148. }
  149. _onChange: Object => void;
  150. /**
  151. * Callback for the onChange event of the field.
  152. *
  153. * @param {Object} evt - The static event.
  154. * @returns {void}
  155. */
  156. _onChange(evt: Object) {
  157. this.setState({
  158. [evt.target.name]: evt.target.value
  159. });
  160. }
  161. /**
  162. * Renders an optional message, if applicable.
  163. *
  164. * @returns {ReactElement}
  165. * @private
  166. */
  167. renderMessage() {
  168. const {
  169. _configHosts: configHosts,
  170. _connecting: connecting,
  171. _error: error,
  172. _progress: progress,
  173. t
  174. } = this.props;
  175. const { username, password } = this.state;
  176. const messageOptions = {};
  177. let messageKey;
  178. if (progress && progress < 1) {
  179. messageKey = t('connection.FETCH_SESSION_ID');
  180. } else if (error) {
  181. const { name } = error;
  182. if (name === JitsiConnectionErrors.PASSWORD_REQUIRED) {
  183. const { credentials } = error;
  184. if (credentials
  185. && credentials.jid === toJid(username, configHosts)
  186. && credentials.password === password) {
  187. messageKey = t('dialog.incorrectPassword');
  188. }
  189. } else if (name) {
  190. messageKey = t('dialog.connectErrorWithMsg');
  191. messageOptions.msg = `${name} ${error.message}`;
  192. }
  193. } else if (connecting) {
  194. messageKey = t('connection.CONNECTING');
  195. }
  196. if (messageKey) {
  197. return (
  198. <span>
  199. { translateToHTML(t, messageKey, messageOptions) }
  200. </span>
  201. );
  202. }
  203. return null;
  204. }
  205. /**
  206. * Implements {@Component#render}.
  207. *
  208. * @inheritdoc
  209. */
  210. render() {
  211. const {
  212. _connecting: connecting,
  213. t
  214. } = this.props;
  215. const { password, loginStarted, username } = this.state;
  216. return (
  217. <Dialog
  218. hideCloseIconButton = { true }
  219. okDisabled = {
  220. connecting
  221. || loginStarted
  222. || !password
  223. || !username
  224. }
  225. okKey = { t('dialog.login') }
  226. onCancel = { this._onCancelLogin }
  227. onSubmit = { this._onLogin }
  228. titleKey = { t('dialog.authenticationRequired') }
  229. width = { 'small' }>
  230. <TextField
  231. autoFocus = { true }
  232. className = 'input-control'
  233. compact = { false }
  234. label = { t('dialog.user') }
  235. name = 'username'
  236. onChange = { this._onChange }
  237. placeholder = { t('dialog.userIdentifier') }
  238. shouldFitContainer = { true }
  239. type = 'text'
  240. value = { username } />
  241. <TextField
  242. className = 'input-control'
  243. compact = { false }
  244. label = { t('dialog.userPassword') }
  245. name = 'password'
  246. onChange = { this._onChange }
  247. placeholder = { t('dialog.password') }
  248. shouldFitContainer = { true }
  249. type = 'password'
  250. value = { password } />
  251. { this.renderMessage() }
  252. </Dialog>
  253. );
  254. }
  255. }
  256. /**
  257. * Maps (parts of) the Redux state to the associated props for the
  258. * {@code LoginDialog} component.
  259. *
  260. * @param {Object} state - The Redux state.
  261. * @private
  262. * @returns {Props}
  263. */
  264. function mapStateToProps(state) {
  265. const {
  266. error: authenticateAndUpgradeRoleError,
  267. progress,
  268. thenableWithCancel
  269. } = state['features/authentication'];
  270. const { authRequired } = state['features/base/conference'];
  271. const { hosts: configHosts } = state['features/base/config'];
  272. const {
  273. connecting,
  274. error: connectionError
  275. } = state['features/base/connection'];
  276. return {
  277. _conference: authRequired,
  278. _configHosts: configHosts,
  279. _connecting: connecting || thenableWithCancel,
  280. _error: connectionError || authenticateAndUpgradeRoleError,
  281. _progress: progress
  282. };
  283. }
  284. export default translate(reduxConnect(mapStateToProps)(LoginDialog));