Login.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. import { Component, createRef } from "inferno";
  2. import * as matrixcs from "matrix-js-sdk";
  3. import * as localforage from "localforage";
  4. import Header from "./ui/Header";
  5. import TextInput from "./ui/TextInput";
  6. import SoftKey from "./ui/SoftKey";
  7. import TextListItem from "./ui/TextListItem";
  8. import ListView from "./ListView";
  9. import requestFunc from "./requestFunc";
  10. import colors from "KaiUI/src/theme/colors.scss";
  11. class Login extends Component {
  12. updateHomeServer = (evt) => {
  13. this.setState({ homeserver: evt.target.value });
  14. };
  15. updateUsername = (evt) => {
  16. this.setState({ username: evt.target.value });
  17. };
  18. updateLoginData = (evt) => {
  19. this.setState({ loginData: evt.target.value });
  20. };
  21. updateCursor = (cursor) => {
  22. this.setState({
  23. cursor: cursor,
  24. });
  25. };
  26. handleKeyDown = (evt) => {
  27. if (evt.key === "Backspace") {
  28. if (this.state.selectedLoginMethod) {
  29. this.setState({ selectedLoginMethod: "" });
  30. evt.preventDefault();
  31. } else if (this.state.loginFlows.length) {
  32. this.setState({ loginFlows: [] });
  33. evt.preventDefault();
  34. }
  35. }
  36. };
  37. doLogin = () => {
  38. if (this.state.loginData) {
  39. this.doLoginFinal();
  40. } else if (this.state.loginFlows.length === 0) {
  41. this.doLoginFlows();
  42. } else {
  43. this.doLoginData();
  44. }
  45. };
  46. doLoginFlows = () => {
  47. let homeserver, username;
  48. homeserver = this.state.homeserver;
  49. username = this.state.username;
  50. if (!homeserver || !username) {
  51. return;
  52. }
  53. if (!homeserver.startsWith("https://")) {
  54. homeserver = "https://" + homeserver;
  55. }
  56. window.mClient = matrixcs.createClient({
  57. baseUrl: homeserver,
  58. request: requestFunc,
  59. });
  60. window.mClient.loginFlows().then((result) => {
  61. this.setState({ loginFlows: result.flows });
  62. });
  63. };
  64. doLoginData = () => {
  65. const loginMethod = this.state.loginFlows[this.state.cursor];
  66. console.log(`[doLogindata] User wanna login with ${loginMethod.type}`);
  67. switch (loginMethod.type) {
  68. case "m.login.password":
  69. console.log("[doLoiginData] Logging in with password");
  70. this.setState({ selectedLoginMethod: "m.login.password" });
  71. break;
  72. default:
  73. alert("This login method is currently not supported :(");
  74. break;
  75. }
  76. };
  77. doLoginFinal = () => {
  78. switch (
  79. this.state.selectedLoginMethod // eslint-disable-line default-case
  80. ) {
  81. case "m.login.password":
  82. window.mClient
  83. .loginWithPassword(
  84. `@${this.state.username}:${this.state.homeserver}`,
  85. this.state.loginData
  86. )
  87. .then((result) => {
  88. localforage.setItem("login", result).then(() => {
  89. alert(`Logged in as ${this.state.username}`);
  90. window.location = window.location; // eslint-disable-line no-self-assign
  91. });
  92. })
  93. .catch((err) => {
  94. console.log("[doLoginFinal] Login Error", err.toString());
  95. switch (err.errcode) {
  96. case "M_FORBIDDEN":
  97. alert("Incorrect login credentials");
  98. break;
  99. case "M_USER_DEACTIVATED":
  100. alert("This user has been deactivated");
  101. this.setState({ loginData: null, loginFlows: [] });
  102. break;
  103. case "M_LIMIT_EXCEEDED":
  104. const retry = err.retry_after_ms / 1000;
  105. alert(`Too many requests! Please retry after ${retry} seconds`);
  106. break;
  107. default:
  108. alert("Login failed");
  109. break;
  110. }
  111. });
  112. }
  113. };
  114. constructor(props) {
  115. super(props);
  116. this.headerRef = createRef();
  117. this.listViewRef = createRef();
  118. this.state = {
  119. homeserver: "",
  120. username: "",
  121. loginFlows: [],
  122. selectedLoginMethod: "",
  123. loginData: null,
  124. cursor: 0,
  125. };
  126. this.listView = (
  127. <ListView
  128. cursor={0}
  129. cursorChangeCb={this.updateCursor}
  130. ref={this.listViewRef}
  131. $HasNonKeyedChildren
  132. >
  133. <TextInput
  134. label="Homeserver"
  135. placeholder="https://matrix.org"
  136. onChange={this.updateHomeServer}
  137. defaultValue={this.state.homeserver}
  138. />
  139. <TextInput
  140. label="Username"
  141. placeholder="somebody"
  142. onChange={this.updateUsername}
  143. defaultValue={this.state.username}
  144. />
  145. </ListView>
  146. );
  147. this.header = (
  148. <Header
  149. text={""}
  150. backgroundColor={colors.headerBlue}
  151. ref={this.headerRef}
  152. />
  153. );
  154. }
  155. componentWillUpdate(nextProps, nextState, context) {
  156. if (nextState.selectedLoginMethod) {
  157. switch (nextState.selectedLoginMethod) {
  158. case "m.login.password":
  159. this.headerRef.current.setState({ text: "Login with password" });
  160. this.listViewRef.current.resetChildrenCursor([
  161. <TextInput
  162. label="Password"
  163. fieldType="password"
  164. placeholder="HG#$@B#@#G%N&*"
  165. onChange={this.updateLoginData}
  166. />,
  167. ]);
  168. break;
  169. default:
  170. break;
  171. }
  172. } else if (nextState.loginFlows.length) {
  173. if (this.headerRef.current.state.text === "Login method") return;
  174. this.headerRef.current.setState({ text: "Login method" });
  175. this.listViewRef.current.resetChildrenCursor(
  176. nextState.loginFlows.map((item) => <TextListItem primary={item.type} />)
  177. );
  178. }
  179. }
  180. render() {
  181. if (this.state.loginFlows.length === 0) {
  182. this.headerRef.current &&
  183. this.headerRef.current.setState({ text: "Login" });
  184. }
  185. return (
  186. <div>
  187. {this.header}
  188. {this.listView}
  189. <footer $HasVNodeChildren>
  190. <SoftKey
  191. leftText="Quit"
  192. leftCb={() => window.close()}
  193. centerCb={this.doLogin}
  194. centerText="Login"
  195. />
  196. </footer>
  197. </div>
  198. );
  199. }
  200. }
  201. export default Login;